算法(二)暴力枚举

时间:2025-02-19 07:45:48

一、概述

1、概念

枚举,顾名思义,就是将所有情况都举出,并判断其是否符合题目条件。所以枚举的基本方法便是分析题意后,找到一个合适的维度列举每一个元素,以完成题目。其中如何找到一个合适的维度来进行枚举便是其中的最大难点

2、枚举的基本条件

(1)时间条件

枚举范围小或时间无限制(蓝桥杯填空题)

一般来说主流的OJ当中,1000ms的时间限制下可以运行操作数为107以内的运算(通常106以内较为保险),所以在采用枚举方法之前最好看一下数据范围,确保整个程序的执行操作数不会超过106-107这个量级,如果超过了就尝试更换枚举的维度或者使用其他算法吧。

(2)编程上的实现条件

在编程实现上,一般来说暴力枚举需要两个条件,一是枚举的范围一般需要连续,如果枚举范围是离散的,那么一般很难使用for循环枚举出所有状态,也就不能保证解的完整性(不过有些时候数据看似离散,但实际上可以经过处理变得连续,如构造数组)。第二个条件是枚举内容需要已知,不能在枚举到某个地方的时候出现未知(不过这个一般都被满足)。

3、不适用的情况

(1)简单的暴力枚举一般只适用于解决简单的问题

(2)难以求出或范围特别大,明显超时(数论等)

/problem/P1403

4、解题思路

(1)找到枚举变量**(尽量由已知推未知)**

(2)找到并尽量缩小范围(根据题意但无需太过精确,可以过大,条件在if中判断即可

(3)依据题意进行操作**(尽量剪枝)**

二、数学运算问题

技巧

(1)枚举变量

按位数0-9(一),按数字(二),或离散时构造数组(一、2)

尽量由已知推未知:一般有一个变量可以直接运算得出。尽量用乘法(二、1)

(2)常见限制

位数、位数字出现次数

(3)取位方法

//取b由低到高位
for(int i = 0;b != 0;i++)
{
    a[i] = b % 10;
    b /= 10;
}

(4)记录位数字出现个数

使用数组a[10],a[i]代表数字i出现了a[i]次

(5)从小到大

从小到大枚举(一、4)

(一)按位枚举

适用于条件偏向于位

1、4个不同的数字

org[i]代表org[i]已出现

/weixin_38172774/article/details/87354310

2、数码管的标价

/weixin_38172774/article/details/87354310

思路:先找一个,然后继续找另一个

枚举变量:离散可构造数组

3、对着数字发呆

/weixin_38172774/article/details/87354310

4、四平方和定理

/weixin_38172774/article/details/87354310

从小到大:从小到大枚举

#include <iostream>
#include <>

using namespace std;

int main() {
    int n,temp;
    int a,b,c;
    //注意类型!
    double d;

    cin >> n ;

    for(a = 0 ; a*a <= n; a++){
        for(b = a ; b*b <= n; b++){
            for(c = b ; c*c <= n; c++){
                d = sqrt(n-a*a-b*b-c*c);
                //判断d是否为整数!
                if(d == int(d)){
                    cout << a<<" "<<b<<" "<<c<<" "<<d;
                    //输出第一个表示法!
                    return 0;
                }
            }
        }
    }
}


5、请猜谜

/weixin_38172774/article/details/87354310

注意判断是否为6位数

(二)数字枚举

适用于数字x,y或数字并非刚好为临界(如1111)

1、abcde/fghij=n

/weixin_38172774/article/details/87354310

b/a = n得b = a * n;避免小数的出现,自然就节约判断了

可以确定:a来说最小为01234,最大为98765

2、火柴棒等式

由于题目中已经给出,最多有24根火柴,而等号和加号各用4根的前提下,A\B\C三个数则总共只有20根火柴,数据范围较小,可以用枚举法枚举A、B。这个时候我们发现,0-9这10个数字所用的火柴数为:6,2,5,5,4,5,6,3,7,6,很明显数字1用的火柴棒最少只要2根,不妨让B为1,那么A和C最多可以使用18根火柴,而C>=A,满足条件的A的最大取值为1111。所以枚举A和B的范围是从0~1111。(应该是过大了)

#include <>
#include <>
int a[2223]= {6,2,5,5,4,5,6,3,7,6};//前10需要的火柴数目
const int b[10]= {6,2,5,5,4,5,6,3,7,6};//定义b数组的值,不可改变
int need(int n)
{
    int t, num;
    num=0;
    if(n==0) return 6;
    while(n>0)
    {
        t=n%10;
        num+=b[t];//统计数字并且把这些数字所需要的火柴数目加起来
        n/=10;//跳过最后一个数字
    }
    return num;
}
int main( )
{
    int n,i,j,A,B,C,D,sum;
    scanf("%d",&n);
    sum=0;
    //预处理
    for(i=10; i<2223; i++)
        a[i]=need(i);//把每个数字需要的火柴数目赋值
    for(i=0; i<=1111; i++)
    {
        for(j=0; j<=1111; j++)
        {
            A=a[i];//A的火柴数目
            B=a[j];//B的火柴数目
            C=n-4-A-B;//C的火柴数目
            D=a[i+j];//等式的实现是通过i+j=(i+j)
            if(D==C) sum++;//如果符合等式
        }
    }
    printf("%d\n",sum);
    return 0;
}
 

3、uva10976

y的范围为 k<y<2k

注意判断x是否为整数 k*y%(y-k)) == 0

/rhythmic/p/

三、模拟+暴力枚举

技巧

依据题意即可

1、机器人繁殖

/weixin_38172774/article/details/87354310

#include <iostream>

using namespace std;

int main() {
    int n;
    long long total,count = 0,thisYear=0,lastYear=0 ;
    cin >> n >>total;
    //暴力枚举可能的最初值
    for (int i = 1; i < total; ++i) {
        count = i; thisYear = i;
        for (int j = 0; j < n; ++j) {
            lastYear = thisYear;
            thisYear = lastYear*2-1;
            count+= thisYear;
        }
        if (count == total) {
            cout << i;
            return 0;
        } else continue;
    }
}

2、HDU1422 重温世界杯

/?pid=1422

/rhythmic/p/

哪里用到那个结论了?

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 200000 + 5;
int a[maxn];
int main()
{
     int n;
     while(scanf("%d",&n) != EOF)
     {
            //存净收入
            for(int i = 1;i <= n;i++)
            {
                  int x , y;
                  scanf("%d%d",&x,&y);
                  a[i] = x - y;
                  //拓展数组以便模拟循环!
                  a[n+i] = x - y;
            }
            int num = 0;
            int Max = 0;
            //现在的钱
            int ans = 0;
             //遍历整个数组!
             for(int i = 1;i <= 2*n;i++)
             {
                 ans += a[i];
                 if(ans >= 0)
                      num++;
                 else
                 {
                       num = 0 , ans = 0;
                 }
                 Max = max(num , Max);
                 if(Max == n) break;
             }

             printf("%d\n",Max);
     }
}

3、滑雪课程设计

对最矮进行枚举

/problem/P3650