注意:状态压缩的集合是无法满足贪心的排序的
一.了解状态压缩之前需要知道的几点
1.(k&-k)在状态压缩和树状数组中都经常能够看到,那么(k&-k)的值是什么含义呢?
这个值是把k的二进制的高位1全部清空,只留下最低位的1,当然如果只有一位1,则保留等于k本身。该操作就是留下k二进制数中最低位的一个1
2.两种相等的形式:i-(i&-i) = i^(i&-i)
他们都表示减去最低位的1,再返回值
3.状态压缩的方法,就是利用二进制数的零一进行模拟。零代表没取,一代表取了。时间复杂度O(2^n)只能用在n小于20的情况下。首先在存储每个元素的时候就按照(1,10,100,1000)的方法来存储,需要枚举所有的子集时可以利用sum[i] = sum[i-(i&-i)] + a[i & -i] 每个sum[i],都表示i在二进制中1取零不取。可以进行初始化,算出每个i的二进制一的个数。
4.代码:
//bc[i]表示i的二进制表示中一的个数是多少
bc[0] = 0;
for (i=1; i<(1<<20); i++)
bc[i] = bc[i-(i&-i)] + 1;
for (i=0; i<m; i++) scanf("%d", &tmp[1<<i]);//tmp存储的时候就是按照1左移存储的
sum[0] = 0;
for (i=1; i<(1<<m); i++)
sum[i] = sum[i-(i&-i)] + tmp[i&-i];/*每次都保证减去最低位后的状态是已经求出的,或者状态是空sum[0]。比如从1000开始到1001(1000求出)到1010(1000求出)到1011(减去最低位后为1010也已求出)。那么为什么地位减去后一定是已求出的呢,因为减去小于其本身,sum[i]又是顺序遍历的
此处还有问题,为什么是不重不漏的呢?每次到临界值(1,10,100,1000)时sum[i]的值都等于tmp[i].当减去最低位后,不肯能是等于它本身的,此时又再加上了另一个数,
注意此处的1,0是二进制。实际上是sum[1] = tmp[1],sum[10] = tmp[10],sum[11] = sum[10] + temp[1] = temp[10] + temp[1],以此类推sum[1111] = temp[1] + temp[10] + temp[100] + temp[1000]
其实很简单的啦,每次sum[i]表示的都是对于i的二进制数来说0,代表取到1代表取不到。肯定是全部的状态(不包括都取不到的,那种是sum[0]的情况)*/
5.一定要注意的问题:
原数组,存储所有子集的数组,还有用来存储任意个数二进制个数的数组大小都要开到2^n次方。另,当n <= 20时,时间空间都可以容纳2^20