状态压缩动态规划:
动态规划的状态有时候比较恶心,不容易表示出来,需要用一些编码技术,把状态压缩的用简单的方式表示出来。
典型方式:当需要表示一个集合有哪些元素时,往往利用2进制用一个整数表示。
再普及一下位运算:
& 按位与 如果两个相应的二进制位都为1,则该位的 结果值为1,否则为0
| 按位或 两个相应的二进制位中只要有一个为1,该位 的结果值为1
^ 按位异或 若参加运算的两个二进制位值相同则为0, 否则为1
~ 取反 ~是一元运算符,用来对一个二进制数按位取 反,即将0变1,将1变0
<< 左移 用来将一个数的各二进制位全部左移N位, 右补0
>> 右移 将一个数的各二进制位右移N位,移到右端 的低位被舍弃,对于无符号数,高位补0
好吧,回到这题
这道题也就是俗称的牛吃草问题,【题目大意】一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛(不包括斜着的),即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)
【Input】1<=n<=12,1<=m<=12
【Output】一个mod100000000的整数
一看到如此小的数据范围,又是求方案数,自然而然就联想到了状态压缩的DP。
话不多说,上代码:
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int dp[20][maxn];
int st[maxn];
int maps[maxn];
int judge1(int x){//这里原理是判断当前状态是否存在两个1相邻,根据&的性质,只有两个都为1才返回1
return x&(x<<1);
}
int judge2(int x,int y){//判断是否可以将这个状态放到地图上
return (maps[x] & st[y]);
}
int main(){
int i,j,k,m,n;
int x,y,z;
while(scanf("%d%d",&m,&n)!=EOF){
memset(maps,0,sizeof(maps));
memset(dp,0,sizeof(dp));
memset(st,0,sizeof(st));
for(i=1;i<=m;i++){
for(j=1;j<=n;j++){
scanf("%d",&x);
if(!x) maps[i] += (1<<j-1);
}
}
int cnt = 0;
for(i=0;i<(1<<n);i++){
if(!judge1(i)){//不停地去除没用的状态
st[++cnt] = i;
}
}
for(i=1;i<=cnt;i++){
if(!judge2(1,i)){
dp[1][i] = 1;
}
}
for(i=2;i<=m;i++){
for(j=1;j<=cnt;j++){
if(judge2(i,j))continue;//枚举这一层可能存在的状态
for(k=1;k<=cnt;k++){
if(!judge2(i-1,k)){//枚举上一层存在的状态
if(!(st[k]&st[j]))判断上下相邻是否可放
dp[i][j]+=dp[i-1][k];//状态转移
}
}
}
}
int ans=0;
for(i=1;i<=cnt;i++){
ans=(ans+dp[m][i]%100000000)%100000000;
}
printf("%d\n",ans%100000000);
}
return 0;
}