题目链接:Corn Fields
Description
给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,
问你有多少种放法。(1<=n,m<=12)
Solution
状态压缩dp入门题
定义状态dp【i】【j】,第 i 行状态为 j 的时候放牛的种数。j 的话我们转化成二进制,从低位到高位 1 表示
放牛,0表示没有放牛,就可以表示一行所有的情况。
转移方程 dp【i】【j】=sum(dp【i-1】【k】)
用 x & (x<<1)来判断一个数相邻两位是不是同时为1,假如同时为 1 则返回一个值,否则返回 0 ,这样就能优化掉一
些状态。对于上下相邻两行状态i,j, i&j==0即可保证上下相邻能放牛的位置不同时放牛。&还用于判断当前状态在当前行是否满足。
Code
#include <cstdio>
#include <cstring>
const int N = 12;
const int M = 1<<N;
const int mod = 1e8;
int st[M+2],map[M+2];
int dp[N+2][M+2]; //表示在第i行状态为j时候可以放牛的种数
inline bool judge1(int x) //判断二进制有没有相邻的1
{
return (x&(x<<1));
}
inline bool judge2(int i,int x)
{
return (map[i]&st[x]); //返回不为0表示当前状态在第i行不能放牛的位置放了牛或
//前后相邻两行相邻位置放了牛
}
int main()
{
int n,m,x;
while(~scanf("%d%d",&n,&m))
{
memset(st,0,sizeof(st));
memset(map,0,sizeof(map));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++){
scanf("%d",&x);
if(x==0)
map[i]+=(1<<(j-1));
}
}
int k=0;
for(int i=0;i<(1<<m);i++){
if(!judge1(i))
st[k++]=i;
}
for(int i=0;i<k;i++)
{
if(!judge2(1,i))
dp[1][i]=1;
}
for(int i=2;i<=n;i++)
{
for(int j=0;j<k;j++)
{
if(judge2(i,j)) //判断第i行 按状态j放牛的话行不行。
continue;
for(int f=0;f<k;f++) //枚举上一行的状态
{
if(judge2(i-1,f)) //判断上一行与其状态是否满足
continue;
if(!(st[j]&st[f]))
dp[i][j]+=dp[i-1][f];
}
}
}
int ans=0;
for(int i=0;i<k;i++){
ans+=dp[n][i];
ans%=mod;
}
printf("%d\n",ans);
}
return 0;
}