POJ 2411 Mondriaan's Dream

本文提供了一种使用动态规划解决POJ 2411问题的方法,该问题要求利用高2宽1的矩形块填充特定大小的矩形区域。文章详细解释了状态转移方程,并提供了完整的C++代码实现。

题目链接:http://poj.org/problem?id=2411

大致题意的话就是用高2宽1的矩型去去构成给定宽高的矩型,具体的话我是看http://blog.csdn.net/hopeztm/article/details/7841917

代码的注释比较详细(因为不详细我都看不懂QAQ)

//注意:x&temp如果为0,那么 x&temp==0 所得也是0,只有(x&temp)==0才是true
#include<stdio.h>
#include<string.h>
#define MEM(x,y) memset(x,y,sizeof(x))

int h,w;
int totalstatu;			//记录2的w次方有多少个状态,因为至多2的11次方,可以用Int表示
long long dp[15][2050];		//dp[i][j]记录第i行的状态为j时共有多少种可能,2^11 = 2048

bool JudgeFirstline(int x)	//判断第一行的状态能否为x
{
	int i = 0;				//i代表第i位为1
	while(i < w)
	{
		if(x & (0x1<<i))		//把x与2的i次方 按位与 ,二进制的x在第i位为1的话
		{
			if(i == w-1)		//最后一位不能是1,否则第一行填满不了
				return false;
			if( (x & (0x1<<(i+1)) ) == 0)	//如果第i+1位是0的话,第一行也不能填满
				return false;
			i += 2;			//如果第i+1位是1的话,就是在第i位和第i+1为横着放了一块砖,直接跳到i+2比较即可
		}
		else
			i += 1;			//如果第i位是0的话,代表其在第i位竖着放了一块砖,所以跳到i+1比较
	}
	return true;
}

bool fit(int line,int x,int y)	//判断第line行的x状态是否与第line-1行的状态y是否冲突
{
	int i = 0;			//i代表第i位为1
	while(i < w)
	{
		int temp = 0x1 << i;		//temp是2的i次方
		if( (x&temp) == 0 )			//第line行在第i位是0
		{
			if( (y&temp) == 0)	//如果第line-1行在第i位也是0,必错
				return false;
			i++;				//如果第line-1行在第i位为1,看i++位
		}
		else					//第line行在第i为为1,必然只能横着在第i位和第i+1位铺砖
		{
			if( (y&temp) == 0 )	//第line行为1,第line-1行为0,竖着铺砖
				i++;
			else				//第line-1行为1
			{
				if(i == w-1)	//如果i是最后一列,不能横着铺砖了
					return false;
				if( (x& 1<<(i+1)) && (y&1<<(i+1)) )	//在第line行第i列和第i+1列横着铺,
													//同时第line行和第line-1行的第i+1列也必须全是1
						i+=2;
				else
					return false;
			}
		}
	}
	return true;
}

int main()
{
	while(scanf("%d%d",&h,&w) != EOF && (h != 0 || w != 0 ))
	{
		MEM(dp,0);
		if(h < w)
		{
			int t = h; h = w;w = t;		//交换h,w是的其列数最小,算是个小优化
		}
		totalstatu = 0x1<<w;	//共有2的w-1次方的状态,即0->(1<<(w-1)-1)

		for(int i = 0 ; i < totalstatu ; i ++)	//初始化第一行的dp
		{
			if(JudgeFirstline(i))
				dp[1][i] = 1;
		}

		for(int i = 2; i <= h ; i ++)	//枚举第i行
		{
			for(int j = 0; j < totalstatu ; j++)	//枚举第i行的每一种状态
			{
				for(int k = 0 ; k < totalstatu ;k++)	//枚举第i-1行的每一种状态
				{
					if(fit(i,j,k))
						dp[i][j] += dp[i-1][k];
				}
			}
		}

		printf("%lld\n",dp[h][totalstatu-1]);	//易知,最后一行必然全是1,所以求dp[h][totalstatu-1]即可
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值