动态规划之状态压缩,实际就是灵活运用位运算。
一个数,转换成2进制,其每一个位的0或者1,都好比是一个开关。这样可以通过灵活的位操作来简化复杂的状态转换。
例如:数字 7,二进制为 111。111 的状态,可以通过 110 | 001 , 101 | 010 , 011 | 100 这三种情况
如何获得一个数的二进制最后一个1的位置?公式: x & (-x)。例如, (10) & (-10) = 2,二进制:(1010) & (-0110) = 0010
这道题,就是将集合里的包含的数,用位来表示,例如,某集合A为 {1,4,7},就可以用 01001001 来表示,位为1的,就表示集合有这个元素。集合B为{3, 7},表示为01000100,那么A和B两个集合合并的结果就是:01001001 | 01000100 = 01001100 ,即新的集合为{3, 4, 7}。很巧妙是吧,看下面的代码,自己慢慢琢磨体会。
#include "stdio.h"
#include "string.h"
int set[1<<14];
void main(){
int n, m;
int z, i, j, k, w, sum;
freopen("in.txt", "r", stdin);
while(scanf("%d %d", &n, &m)!=EOF){
memset(set, 0, sizeof(set));
for(i=0; i<n; i++){
scanf("%d", &k);
w = 0;
for(j=0; j<k; j++){
scanf("%d", &z);
w += 1<<(z-1);
}
set[w] = 1; //w状态的集合,标记为1,表示已经有了
for(j=1; j<1<<14; j++){
if(set[j]) //若j状态的集合有了,那么将新来的w集合与j集合合并,就可以产生w|j集合。
set[w|j] = 1;
}
}
sum = 0;
for(j=1; j<1<<14; j++){
if(set[j]) //最后计算所有j状态的集合的总数
sum++;
}
printf("%d\n", sum);
}
}
上述程序的过程为:
假如A,B,C,D....这几个集合,依次输入
输入A ,则A集合有了。 //A
输入B,则B集合有了,然后B去和其他集合合并,现在只有一个A集合,那么合并产生A|B。 // A, B, A|B
输入C,则C集合有了,然后C去和其他集合合并,现在有3个其他的集合,那么合并产生…… // A, B, A|B, C, A|C, B|C, A|B|C
输入D,则…… //A, B, A|B, C, A|C, B|C, A|B|C, D, A|D, B|D, A|B|D, C|D, A|C|D, B|C|D, A|B|C|D
同理,……