POJ3254 Corn Fields(状压DP)
dp[i][j]表示第i行第j个状态时,前i行得到的方案数,该状态可由前一行的状态转移过来。
本题重点在于合法性检测:每一行都用一个二进制数表示,1.二进制数不能有相邻的1;2.要和原地图匹配;3.上下两行不能有冲突。
预处理地图时将0换成1,方便进行2号检测,用位运算&可以实现。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 #define mod 100000000 5 int m,n,top=0; 6 int state[600];//储存合法状态 7 int dp[20][600];//dp[i][j]表示第i行,第j种编号时,前i行的可行方案数 8 int cur[20]; 9 10 bool check(int x){//有相邻的1则不合法 11 if(x&x<<1) return 0; 12 return 1; 13 } 14 15 void init(){//记录所有没有相邻1的编号 16 top=0; 17 for(int i=0;i<1<<n;i++)//n个格子,2^n种情况 18 if(check(i)) state[++top]=i; 19 } 20 21 bool fit(int x,int k){ 22 if(x&cur[k]) return 0; 23 return 1; 24 } 25 26 void solve(){ 27 for(int j=1;j<=top;j++) 28 if(fit(state[j],1)) 29 dp[1][j]=1;//处理第1行 30 for(int i=2;i<=m;i++) 31 for(int j=1;j<=top;j++){//state[j]是i行的状态 32 if(!fit(state[j],i)) continue; 33 for(int k=1;k<=top;k++){//state[k]是i-1行的状态 34 if(!fit(state[k],i-1)) continue; 35 if(state[j]&state[k]) continue;//上下行有相邻1 36 dp[i][j]=(dp[i][j]+dp[i-1][k])%mod; 37 } 38 } 39 } 40 41 int main(){ 42 while(~scanf("%d%d",&m,&n)){ 43 init(); 44 memset(dp,0,sizeof(dp)); 45 for(int i=1;i<=m;i++){ 46 cur[i]=0; 47 int num; 48 for(int j=1;j<=n;j++){ 49 scanf("%d",&num); 50 if(num==0) cur[i]+=(1<<(n-j));//0记录为1,方便检测 51 } 52 } 53 solve(); 54 int ans=0; 55 for(int j=1;j<=top;j++) 56 ans=(ans+dp[m][j])%mod; 57 printf("%d\n",ans); 58 } 59 return 0; 60 }