最近在写状压dp,写得不太顺利啊,抠很久才抠出来。可见如此之菜。
状态压缩dp(简称状压dp)是一种非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常之优秀。
状压dp的适用情况就是当状态可以由0或者1来表示时,我们所需状态数组无法满足。将状态表示成二进制数转化成十进制通过位运算的处理来进行状态的转移。
状压dp涉及了一些位运算。有或,与,异或,左移等...
比如:
1.判断一个数字x二进制下第i位是不是等于1。
方法:
if ( ( ( 1 << ( i - 1 ) ) & x ) > 0)
将1左移i-1位,相当于制造了一个只有第 i 位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。
2.将一个数字x二进制下第i位更改成1。
方法:
x = x | ( 1<<(i-1) )。
证明方法与1类似,此处不再重复证明。
3.把一个数字二进制下最靠右的第一个1去掉。
方法:
x=x&(x-1)
将x用二进制表示时最右边的一个1变为0,因为x-1将会将该位(x用二进制表示时最右边的一个1)变为0。
还可以判断x是否是2的n次方,因为如果一个数是2的n次方,那么这个数用二进制表示时其最高位为1,其余位为0。
4.判断某状态两者之间是否相邻。
bool ok(int x){
if(x&x<<1)return 0;
return 1;
}
5.对一个状态取反。
if(num == 0) cur[i] += (i<<(M - j));
6.判断两个状态是否相互合法。
bool fit(int x,int k){
if(x&cur[k])return 0;
return 1;
}
这些位运算在状压dp中几乎都会有用到。
然后....
我们接下来考虑的大部分都是怎么dp法....怎么去考虑状态转移....怎么去搜每一个状态....怎么在搜时再剪一下枝之类的问题了...这个暂时不知道怎么说...还是做多点题就懂了....
Update:
状态压缩DP,状压dp的难点在于状态的表示,状态的表示是否是满足无后效性的,是不是满足最优子结构的。是不是可以很容易的通过位运算的特性去用一个状态得到一个新状态。
状压dp中少不了对状态的操作。再加上我们一般都会用二进制去表示状态,所以我们需要对 C++ 中的位运算部分有些了解。(除了上面的,我们还有...)
1.判断第i为是否为1 (这个位是从右数起,从0开始。)
if (x&(1<<i) {} 或者if ((x>>i)&1) { }
前面两种写法都是可以的,这里要注意不要忘记了位运算两侧的括号。因为位运算的优先级很低。
2.设置第i位为1。
x |= 1<<i;
3.设置第i位为0 。
x &= ~(1<<i);
4.切换第i位。
x ^= 1<<i;
因为状压dp需要遍历每个状态,所以将会出现2^n的情况数量,所以明显的标志就是数据不能太多,然后遍历所有状态的姿势就是用二进制来表示,01串,1表示使用,0表示未使用,就把所有的状态投射到很多二进制的数上,然后对每个状态找上一些合法的状态,这大概就是状压dp吧。