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

题意:给出一个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 __int64

int 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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值