状压dp POJ 3254 Corn Fields
链接:http://poj.org/problem?id=3254
题意:m*n的矩阵,每个格子中都有个0或者1,0表示不能放牧,1表示可以放牧,要求不能出现相邻的牧场都放牧了。求方案总数
注意到这个题的m和n都特别小,都不超过12
那么:想到状态压缩
枚举每一行的所有放牧的方案,用1表示放牧,用0表示不放牧,每一个数的二进制展开均为0和1的形式
那么:所有的状态总数也是很小的:2^12 = 4096种
题目要求:相邻的不能放牧:上下左右称为相邻,也就是说,当前行的枚举判断的条件,只与当前行的状态与上一行的状态有关系。定义dp[i][j]:前i行,且第i行状态为j时的方案数
考虑状态转移:我们需要枚举第i-1行的状态k,首先k是个合法的没有左右放牧相邻的状态,然后再使得j和k在放牧时候不相邻也就是处理了上下关系~
则有:dp[i][j] = sum(dp[i-1][k])
代码如下:
#include <cstdio>
#include <cstring>
using namespace std;
#define mod 100000000
int M,N,tot;
int state[600],num[110];
int dp[20][600];
int cur[20];
bool ok(int x){
if (x & (x<<1)) return false;
return true;
}
void init(){
tot=0;
int maxnum=1<<N;
for(int i=0;i<maxnum;i++)
if (ok(i)) state[++tot]=i;
}
bool fit(int x,int k){
if (x & cur[k]) return false;
return true;
}
int main(){
//freopen("input.txt","r",stdin);
while(scanf("%d%d",&M,&N)!=EOF){
init();
memset(dp,0,sizeof(dp));
for(int i=1;i<=M;i++){
cur[i]=0;
int num;
for(int j=1;j<=N;j++){
scanf("%d",&num);
if (!num) cur[i] += (1<<(N-j));
}
}
for(int i=1;i<=tot;i++)
if (fit(state[i],1))
dp[1][i]=1;
for(int i=2;i<=M;i++)
for(int k=1;k<=tot;k++)
if (fit(state[k],i))
for(int j=1;j<=tot;j++)
if (fit(state[j],i-1))
if (!(state[k] & state[j]))
dp[i][k] = (dp[i][k]+dp[i-1][j])%mod;
int ans=0;
for(int i=1;i<=tot;i++)
ans = (ans+dp[M][i])%mod;
printf("%d\n",ans);
}
return 0;
}