简介
状态压缩动态规划(简称状压DP)是非常典型的一类DP。他是利用二进制来描述状态的一种DP方式,大家都知道,DP是解决多阶段决策最优化问题的思想方法,但是有时候阶段多了,维度多了,数组也就爆了,因为虽然维度多,但是有些空间可能用不到,这就很浪费了,(主要是维度多了处理麻烦很恶心)所以我们就把我们就把一组数据压到一个int变量里面(只要是整形,什么都好啦)
举个生动形象的例子,在01背包中,n个物品的选择方式,就可以用二进制数来表示:1 0 1 1 0(n=5),从低位开始,表示1不选,2选,3选,4不选,5选。
既然它与二进制有关,所以我们就需要讲一讲二进制啦
位运算
基本运算
名称 |
作用 |
举例 |
左移(<<) | 位左移运算将整个数按位左移若干位,左移后空出的部分0。 | 5(101)<<2=20(10100) |
右移(>>) | 位右移运算将整个数按位右移若干位,右移后空出的部分填0。 | 5(101)>>2=1(1) |
按位与(&) | 会将两个十进制数在二进制下进行与运算,然后返回其十进制下的值。在某一位上,只有两个数都是1才返回1 | 5(101)&2(10)=0 |
按位或(|) | 会将两个十进制数在二进制下进行或运算,然后返回其十进制下的值。在某一位上,只要有一个数是1就返回1 | 5(101)|2(10)=7(111) |
按位异或(^) | 会将两个十进制数在二进制下进行异或运算,然后返回其十进制下的值。在某一位上,只有两个数相同才返回1 | 5(101)^2(10)=0 |
按位非(~) | 把0变成1,1变成0,然后返回其十进制下的值。 | 咳咳,最好不要轻易用,因为int有32位(二进制),前面的全部会变成1 |
进阶运算
检查第i位是否是 1
if(1<<(i-1)&x)...
检查第i位是否是 0
if(1<<(i-1)&x==0)...
统计x中有多少个 1
while(x)
{
cnt += x&1;
x >>= 1;
}
检查x中是否有相邻的 1
if(x&(x<<1))...
计算x最低位1代表的值
int lowbit(int x)
{
return x&(-x);
}
把第i位变成 1
x |= (1<<(i-1))
把第i位变成 0
x &= ~(1<<(i-1))
把第i位取反
x ^= (1<<(i-1)
末i位取反
x^(1<<(i-1))
x包含y
if(x&y==y)... OR if(x|y==x)...
取右边连续的1
(x^(x+1))>>1
把右边连续的0变成1
x&(x-1)
把右边连续的1变成0
x|(x+1)
把右边第一个0变成1
x|(x+1)
例题 P1879 [USACO06NOV]玉米田Corn Fields
这是一道典型的状压DP(废话,学的状压不给状压给什么)
我们用一个M数组来存储每一行的情况(土地是否贫瘠),然后用state数组表示某一种种植方法是否可以满足任意两个种植物不相邻,(bool数组)。
然后开始一行一行的枚举状态,如果可用并且没有贫瘠土地,就枚举上一排的,看不相邻的情况就转移,最后把最后一行所有的情况累计起来即可。
例题 P1896 [SCOI2005]互不侵犯
还是和玉米田差不多的操作,但是dp数组需要加一维,所以就成了在某一行的某种状态中已经放了x个国王有几种方法,剩下的就和上一个例题差不多了