解释
开始看不出这是一个状压DP的问题,但是看到其他博客中每种摆放的状态表示的时候,终于想通了这个问题的解法:
状态表示:横着放置:全部为1,竖着放置:上面为0、下面为1
那么我们就可以用二进制表示出所有的状态了,并且当前行的状态只和上一行状态相关,现在就找一下每种状态之间的限制:
1、i-1行为0的相应位置,在i行中必须全部为 1 (因为为0的状态一定为竖着放置,那么他的下面必须为1)
2、i-1行和i行中相应位置同为1的段长度必须为偶数 (因为除去竖着放置的,那么i与i-1行上下必须全部为1,并且这样横着放置的每个都有两个1,那么这样的状态就一定为偶数个)
3、第一行我们只需要判断连续为1的段长为偶数即可,因为没有上面一行竖着放置的限制
dp[i][j] 表示: 第i行状态为j时满足的数量
并且我们不管怎么样最后一行第一定的,最后一行必须全部为1,因为不会再次出现下面一行的信息,也就是不会从最后一行生成一个新的竖放置的,所以我们最终的答案就一定是:dp[n][(1<<m)-1]
程序
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> using namespace std; typedef long long ll; const int N = 12; ll dp[N][1<<N];// int n,m; bool Judge(int x)//判断是否所有连续1的个数都为偶数 { int num = 0; while(x) { if(x & 1) num ++; else { if(num & 1) return false; num = 0; } x >>= 1; } if(num & 1) return false; return true; } int main() { while(scanf("%d%d",&n,&m) && n+m) { memset(dp,0,sizeof(dp)); for(int i = 0;i < (1<<m);i ++) if(Judge(i)) //限制3 dp[1][i] = 1; for(int i = 1;i < n;i ++) for(int j = 0;j < (1<<m);j ++) if(dp[i][j]) { for(int k = 0;k < (1<<m);k ++) if((j | k) == (1<<m)-1 && Judge(j & k))//依次放置限制1、2的发生 dp[i+1][k] += dp[i][j]; } printf("%I64d\n",dp[n][(1<<m)-1]); } return 0; }
参考
http://www.cnblogs.com/kuangbin/archive/2013/04/28/3049684.html