dp[i][j]表示第i行 为j 状态的有多少种方法。
1表示这一列的格子是自己放的,是竖的第一行或横的.如果是0则表示这一个格子是竖的第二格的
就要写一个匹配的函数,保证上一行可以跟这一行凑出来合法的满满的两行。
也就是其他博客里说的。
1、如果第i行中有0,则第i-1行一定为1;
2、如果第i行中为1的x列第i-1行为0,说明第i行肯定是竖着放的;
3、如果第i行中为1的x列第i-1行的该列也为1,可能性只有一个,第i行是横放的,所以第i行的x+1列也必须为1,又因为第i行的x+1列为1是因为横着放的,所以第i-1行的x+1列也必须为1。
那么状态转移就很简单了。
dp[i][cur] += dp[i-1][last] 如果last和cur能匹配的话。
还有输出的时候为什么是输出dp[n][1<<m-1] 。
因为你在这一行是1 上一行是0 等同于这一行是0 上一行是 1
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> typedef long long LL; using namespace std; LL dp[11][1<<11]; int n,m; bool ok(int x) { int pos=1; int count=0; for(int i=0;i<(1<<m);i++,pos<<=1) { if(x&pos)count++; else { if(count&1)return false; else count=0; } } return !(count&1); } bool match(int last,int cur) { for(int pos=1;pos<(1<<m);) { if(!(last&pos) && !(cur&pos))return false; else if((last&pos)&&(cur&pos)) { if(((pos<<1)&last) && ((pos<<1)&cur))pos<<=2; else return false; } else pos<<=1; } return true; } int main() { while(scanf("%d%d",&n,&m)!=EOF && n && m) { if(n<m)swap(n,m); memset(dp,0,sizeof dp); for(int i=0;i<(1<<m);i++) { if(ok(i))dp[0][i]=1; } for(int i=1;i<n;i++) { for(int last=0;last<1<<m;last++) { for(int cur=0;cur<1<<m;cur++) if(match(last,cur)) dp[i][cur]+=dp[i-1][last]; } } cout<<dp[n-1][(1<<m)-1]<<endl; } return 0; }
POJ 2411 Mondriaan's Dream (状压DP)
原文:http://blog.csdn.net/u010709592/article/details/19178021