题目链接: http://poj.org/problem?id=3254
状压dp一般范围都较小,这类dp一般数据范围有一项很小(好像是不超过16吧),看到这种数据范围就可以往状压上想
先提一下位运算, '&' 表示对两个数的二进制进行操作, 相同位如果都为1, 则此位最终结果为1。
y >> x , 表示将数 y的二进制向右移 x 位, 低位舍弃,高位补 0
<< 运算符同理
题意:农夫FJ有一块n行m列的矩形土地, 有的土地是肥沃的,可以在这些土地上放牛(用1表示),有的土地不能放牛(用0表示),而且相邻的可以放牛的格子不能同时有牛,问FJ一共有多少种放牛的方案(一头牛都不放也是一种方案)。
分析:以样例为例,我们可以一行一行的考虑。假如对于每一行,用1表示放牛,0表示不放牛,
第一行的状态为:000,001,010,011(舍弃) 100,101,110(舍弃)111(舍弃)
符合题意的状态只有5个,所以第一行有5种方案。
第二行的状态为:000,010
但是第二行中的010和第一行中的010冲突,所以如果第二行状态为010时,共有4种方案;状态为000时,有5种方案,所以一共有4+5=9种方案。
分析完我们会发现,对于每一行,都可以一个01串来表示这一行的状态,而这个状态可以用一个十进制整数来代替,也就是说,把这个状态压缩成了一个十进制整数,所以称为是状态压缩。
本题中,dp[i][j] 就表示第i行状态为j时的方案数。
状态压缩dp中最常见的就是位运算,因为位运算可以很方便的判断当前状态是否合法。例如本题中判断第i行是不是有两块相邻的土地同时都有牛,假设当前状态为X,那么只需要判断X&(X>>1)或者X&(X<<1)的结果是不是0,如果是0,说明没有相邻的,否则就说明有相邻的。
AC代码:
#include
#include
#include
using namespace std;
const int mod = 100000000;
const int maxn = 1 << 12 + 4; // + 太多会 MLE
int dp[14][maxn]; //到第i行,j状态满足条件的个数
vectorv[14]; //存放第i行的状态
int mp[14][14]; //存放地图
int n , m;
//根据第x行 mp[x][j] 的值, 将其二进制取反(即0为1,1为0) 算得十进制的数,即可以排除在不能种植的地方种植的方案
int state(int x){
int s = 0;
int tmp = 1;
for(int i = m;i >= 1;-- i){
s += (!mp[x][i]) * tmp;
tmp *= 2;
}
return s;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
memset(dp,0,sizeof(dp));
memset(v,0,sizeof(v));
memset(mp,0,sizeof(mp));
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= m;j ++)
scanf("%d",&mp[i][j]);
}
v[0].push_back(0); //第一行不会出现与上一行相邻的情况
int k = 1 << m; //如果列数为m, 则其出现二进制最大的情况为 2的m次方 - 1 ,即 k - 1;
for(int i = 0;i < k;i ++)
dp[0][i] = 1; //第一行除了所有左右相邻的情况后,其他情况都满足
for(int i = 1;i <= n;i ++){
int tmp = state(i);
for(int j = 0;j < k;j ++){
if(j & (j << 1)) continue; //排除j的二进制中含两个相邻的1
if(j & tmp) continue; //通过tmp 求的 mp在此行的 几种子情况(土地为1的点,可种植,可不种植)
v[i].push_back(j); //将满足条件的状态放入v[i]
}
for(int j = 0;j < v[i].size();j ++){
int now = v[i][j]; //从v[i]中提取当前此行的所有状态
for(int f = 0;f < v[i-1].size();f ++){
int last = v[i-1][f]; //从v[i-1]中提取上一行的所有状态
if(now & last) continue; //冲突(即上下相邻)
dp[i][now] = (dp[i][now] + dp[i-1][last]) % mod;
}
}
}
int ans = 0;
for(int i = 0;i < k;i ++)
ans = (ans + dp[n][i]) % mod;
printf("%d\n",ans);
}
return 0;
}