一、概述
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