暴力枚举:是一种列举各种情况,然后判断是否可行的一种做法
具体的解法共有三种
1、循环枚举
类似于模拟,将所有的情况依次罗列出来,然后判断是否可行,并累加得到答案。这种做法是最简单的一种,因此也是最不靠谱的一种做法。因此需要用到剪枝,分治,或者变换枚举对象等。变换枚举对象就是从另一个角度思考解题的方法,例如从枚举位置坐标变为枚举长宽高这样的做法。
2、子集枚举
就是组合数,一共有n个集合,要从中选取k个集合,那么每个集合无非就是存在两种状态,就是在或者不在,那么用二进制表示就是有一个n位的二进制数,从 1 遍历到 2n ,那么就可以将所有状态遍历一遍,然后选出其中有k个1的二进制数就是我们的目的。需要注意n应该小于等于 30 ,否则会超过 int 的数值。那么第二个问题就是如何找出一个数的二进制中有多少个 1 ,需要用到位运算,1 <<( i - 1 ) 表示的是仅包含第i个元素的集合的数字,因此用&运算来查看该位置是否为1,若为1,则结果>0,若为0,则结果=0。包含所有元素的集合数为a=( 1 << n ) - 1;
int U=1<<n; //U表示全集代表的数
for (int i=0; i<U; i++) {
int sum=0;
//内建函数 __builtin_popcount表示的是统计二进制数中有多少个1
if (__builtin_popcount(i)==k) {
for (int j=0; j<n; j++) {
if (i&(1<<j)) {
sum+=a[j];
}
}
}
}
具体位运算的一些性质看这篇文章:强大的位运算
3、排列枚举
就是排列数,这个更简单,就是用c++的一个函数next_permutation,用法很简单,就是将一个数组的开头与结尾的下一个放入该函数,就可以得到该排列的下一个排列(按字典序顺序)。
do {
for (int i=0; i<n; i++) {
printf("%d ",a[i]);
}
printf("\n");
} while (next_permutation(a, a+n));