POJ 2411 Mondriaan's Dream 状态压缩(DP)

来源:互联网 发布:mac校园网客户端 编辑:程序博客网 时间:2024/05/16 15:44

题意:给出一个h*w的矩阵,用长为1宽为2,或者长为2,宽为1的骨牌来填满,求方案数。
题解:放骨牌的时候要么横着放,要么竖着放。现在来规定放置方式,对于矩阵上任意一个点[i,j],在上面放置一块骨牌,如果横着放,那么占据[i,j], [i,j+1];如果竖着放则占据[i,j] ,[i-1,j],并且当第 i 行放置完成后第 i-1之前的所有位置行必须是被骨牌填满的。

#include <algorithm>#include <iostream>using namespace std;#define bit(x) (1<<x)#define lint __int64int state, h, w;lint dp[12][1<<12];bool check ( int st ) /* 检验第一行的放置方案 */{for ( int i = 0; i < w; i++ ){if ( 0 == (st & bit(i)) ) /* 第i点为0,那么留给它的下一行来覆盖,因为竖着放是覆盖点[i,j],[i-1,j],可行 */continue;if ( i == w - 1 ) /* 为1且已经是最右边的点,不可能再横着放下一块骨牌了,不可行 */return false;if ( ! (st & bit(1+i)) ) /* 本身为1且下一个点为0,不满足横着放的条件,因为横着放覆盖点[i,j],[i,j+1],不可行*/return false;i++; /* 到这一步说明是点i横着放下一块骨牌,那么点i,和点i+1都是1,直接跳到i+2(两次i++) */}return true;}bool isLeagal ( int st1, int st2 ) /* 检验状态转移是否合法 */{for ( int i = 0; i < w; i++ ){if ( (st1 & bit(i)) != 0 ) {if ( (st2 & bit(i)) != 0 ){if ( i == w - 1 )return false;if ( 0 == (st2 & bit(i+1)) || 0 == (st1 & bit(i+1)) )return false;i++;}}else{if ( (st2 & bit(i)) == 0 )     return false;}}return true;}int main(){while ( scanf("%d %d", &h, &w) && (h+w) ){if ( (h * w) % 2 ){printf("0\n");continue;}if ( w > h ) swap(w,h);int i, j, k;state = (1 << w) - 1;memset(dp,0,sizeof(dp));for( i = 0; i <= state; i++ )if ( check(i) ) dp[h][i] = 1;for ( i = h-1; i >= 1; i-- ){for ( j = 0; j <= state; j++ ){for ( k = 0; k <= state; k++ )if ( isLeagal ( k, j ) )dp[i][j] += dp[i+1][k];}}printf("%I64d\n",dp[1][state]);}return 0;}