【引言】
小的时候,我们刚开始学习数学的时候,就会先学习里面的数字,老师会教我们1-10的读法以及写法,对于第一个数字1,它就是集合[1,10]中的一个枚举。
【枚举法定义】
在进行归纳推理时,如果逐个考察某类事件的所有可能情况,因而得出一般结论,那么这结论是可靠的,这种归纳方法叫做枚举法。
在枚举过程中将问题的所有可能的答案一一列举,然后根据条件判断此答案是否合适,合适就保留,不合适就丢弃。所以说枚举得到的结果肯定是正确的。但上面也说道了,枚举法对得到的结果逐一进行判断,这样的话时间复杂度是非常高的。而且判断标准对思维的细腻程度的要求其实很高,要做到无遗漏。
许多人对枚举法的第一印象就是“暴力法”“for循环法”,个人感觉枚举是一种思考问题的方式,是一种思想。枚举并不是单纯的利用“for循环”,而是在任何情况下,选准最合适的对象处理对象,这是关键,而选择处理对象是在优化算法,减少求解步骤,缩小解空间前提下选择的。
例题1:HDU 2012--素数判定
题目大意:对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括x,y)(-39<=x
枚举 x--y的所有值,看哪些满足表达式
int main(){
int x,y,i,j,m,z;
while((scanf("%d%d", &x, &y), x || y)){
z=0;
for(i = x; i <= y; i++){
m = i * i + i + 41;
for(j = 2; j <= sqrt(m); j++){
if(m % j == 0)
z++;
}
}
if(z == 0)
printf("OK\n");
else
printf("Sorry\n");
}
return 0;
}
例题2:HDU1172 猜数字
题意:计算机随机产生一个四位数,然后玩家去猜这个四位数,每次猜测,计算机都会告诉你猜对了几个数字,有几个数字在正确的位置上。现在给你N组猜测的结果,那么问题来了:能否根据这几组猜测的结果去确定这个四位数是多少。如果能确定,输出这个四位数。如果不能确定,输出:"Not sure"。
思路:
只是四位数的话,不要想着如何通过已有的条件来得到正确的值,而是枚举0000-9999这10000个数就可以了。找到满足这几组猜测结果的四位数。如果只有1个,就输出这个数。如果是0个或是多于1个,就输出:"Not sure"。
AC代码:
#define maxn 100
using namespace std;
struct Node{
int a,b,c;
};
Node node [maxn];
bool check(int k,int num){
int num1[5], num2[5];
bool vis[5];
for(int i = 1; i <= 4; ++i)
vis[i] = 0;
num1[1] = node[k].a / 1000;
num1[2] = node[k].a % 1000 / 100;
num1[3] = node[k].a % 100 / 10;
num1[4] = node[k].a % 10;
num2[1] = num / 1000;
num2[2] = num % 1000 / 100;
num2[3] = num % 100 / 10;
num2[4] = num % 10;
int ans = 0;
for(int i = 1; i <= 4; ++i)//相同位置相同的数
if(num1[i] == num2[i])
ans++;
if(ans != node[k].c)
return false;//如果个数不符就返回错
ans = 0;
for(int i = 1; i <= 4; ++i){//相同数字的个数
for(int j = 1; j <= 4; ++j){
if(num1[i] == num2[j] && !vis[j]){
vis[j] = true;
ans++; break;
}
}
}
if(ans != node[k].b)
return false;
return true;
}
int main(){
int n;
while(scanf("%d", &n), n){
for(int i = 1; i <= n; ++i)
scanf("%d%d%d", &node[i].a, &node[i].b, &node[i].c);
int ans=0, result;
bool flag = true;
for(int j = 1000; j <= 9999; ++j){//暴力枚举
for(int i = 1; i <= n; ++i){
flag = check(i, j);
if(!flag)
break;
}
if(flag){
ans++;
result = j;
}
}
if(ans == 1){
printf("%d\n",result);
}
else
printf("Not sure\n");
}
return 0;
}
【枚举和其他算法相结合】
HDU 1598--find the most comfortable road【并查集 + 枚举】
题目大意:城市之间通过许多条高速公路相连,每条高速公路有个速度限制,人们在高速公路上行驶时舒适度有要求,在行驶过程中最高速度与最低速度的差越小乘坐越舒服,问最佳路线的舒适度最高速与最低速的差。
思路:本题是并查集和枚举法的结合,我们可以对每条路的速度从大到小进行排序,从速度最小的边开始枚举,把比它速度大的边加入集合,直到起点跟终点属于同一个集合为止,然后看看此时最大边-最小边的差。记录下来,再从速度第二小的边开始枚举,把比它速度大的边加入集合,直到起点跟终点属于同一个集合为止,然后看看此时最大边-最小边的差。记录下来。一直枚举到最后一条边。比较每次枚举时最大边和最小边的差,就可以得到我们要的结果。
AC代码如下:
#define inf 0x3f3f3f3f
using namespace std;
struct node
{
int a,b,c;
};
node map[1100];
int cmp(node s1,node s2){
return s1.c < s2.c;
}
int per[220];
int n,m;
int find(int x){
if(per[x] == -1)
return x;
return per[x] = find(per[x]);
}
int main (){
while(scanf("%d %d", &n, &m)!=EOF){
for(int i = 1; i <= m; ++i)
scanf("%d %d %d", &map[i].a, &map[i].b, &map[i].c);
sort(map + 1, map + 1 + m, cmp);
int Q;
scanf("%d", &Q);
int st,ed;
while(Q--){
int MIN = inf;
scanf("%d %d",&st, &ed);
//枚举最小边,再从小到大一直加大于等于这个最小边的其他边,直到起点跟终点属于同一个集合为止,然后用最大边-最小边的差
for(int i = 1; i <= m; ++i){
memset(per, -1, sizeof(per));
for(int j = i; j <= m; ++j){
int fa = find(map[j].a);
int fb = find(map[j].b);
if(fa != fb)
per[fb] = fa;
if(find(st) == find(ed)){
MIN = min(MIN, map[j].c - map[i].c);
break;
}
}
}
if(MIN == inf)
printf("-1\n");
else
printf("%d\n", MIN);
}
}
return 0;
}
POJ 3080 Blue Jeans 【KMP &&暴力枚举】
题意:给你N个串,让你找出最长公共子串(长度必须大于2)。若有长度相同的子串,输出字典序较小的。
思路:后缀数组可以解决这个问题,但是没有必要用后缀数组,我们可以枚举一个串的所有子串,判断该子串有没有在其它串中出现。若在其它串中全部出现 则更新子串,否则枚举下一个子串。
using namespace std;
char str[15][100];
int next[100];
void getnext(char *s1){
int j = 0, k = -1;
int len = strlen(s1);
next[0] = -1;
while(j < len){
if(k == -1 || s1[j] == s1[k]){
++j;
++k;
next[j] = k;
}
else k = next[k];
}
return ;
}
bool kmp(char *s1, char *s2){
int len1 = strlen(s2);
int len2 = strlen(s1);
getnext(s1);
int i = 0, j = 0;
while(i < len1){
if(j == -1 || s1[j] == s2[i]){
++i;
++j;
}
else j=next[j];
if(j == len2)
return true;
}
return false;
}
int main(){
int T;
scanf("%d", &T);
char temp[100];
char sum[100];
while(T--){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i)
scanf("%s", str[i]);
int len = strlen(str[0]);
memset(sum, '\0', sizeof(sum));
for(int i = 0; i < len; ++i){
int ans = 0;
for(int j = i; j < len; ++j){
//枚举一一个串的所有子串,存储在新串中
temp[ans++] = str[0][j];
temp[ans] = '\0';
int flag = 1;
for(int k = 1; k < n; ++k){//新的串和str[]匹配
if(!kmp(temp, str[k])){//匹配失败
flag = 0;
break;
}
}
if(flag){//匹配成功
//最长公共子串而且字典序最小
if(strlen(temp) > strlen(sum))
strcpy(sum, temp);
else if(strlen(temp)==strlen(sum) && strcmp(temp, sum) < 0)
strcpy(sum, temp);
}
}
}
if(strlen(sum) < 3)
printf("no significant commonalities\n");
else
printf("%s\n", sum);
}
return 0;
}
本文由金磊提供,适用于ACM入门者,未经允许,禁止转载。
还木有回答哦,快抢占第一把交椅吧!