2013寒假练习 1022 Mondriaan's Dream

地址:http://acm.bit.edu.cn/mod/programming/view.php?id=673

题意:求用1*2的砖填满m*n的矩阵的方法总数,对称的也算。

状态压缩DP。。由于接触的比较少是模仿别人AC代码写的。

首先可以简化这道题的关键之一是可以把每一行的状态记成101000(最多11位)之类的二进制表示,其中0表示该行的这一位要么是上一行的竖块占掉的,要么是这一行横放的占掉的,这两种都是不会影响下一行的情况。1表示这个位置放了一个竖块,所以会影响到下一行。那么,一旦确定了每一行的二进制表示,整个矩阵的放法也就唯一确定。(重要)。

我们设一个虚拟的第0行。那么该行的合法状态只有0一种。(即00000)每行可能的状态只有0到2^n-1(即从00..000到11....111),而根据上一行的状态可以推出下一行的合法状态,因为上一行和下一行状态合法(不冲突)需满足一定条件(见代码)。因此通过类似dfs的搜索可以从第0行推到第m行。第m行也是特殊的,其合法状态也只有0这一种。

如果第m-1行的状态和第m行的“0”不冲突,那么就返回1,否则返回0,然后层层返回回去,得到dfs(0,0)的答案。

但是这样有很多重复计算会超时,所以就用状态压缩的dp加记忆化搜索优化,即得出第k行状态为a的方法数的答案后,存入dp[a][k]中,下次就不用再递归搜索了。

参考的代码地址:http://www.cnblogs.com/goagain/archive/2012/12/05/2802772.html

我的代码:

#include<iostream>
#define ll long long
int m,n;
ll dp[(1<<11)+5][12];
bool judge(int a,int b)               //判断相邻两行,状态分别为a和b,是否合法
{
	bool ans=0;
	if(a&b) return false;               //若a与b不为0,说明两行在某相同位置均为1,不合法
	int k=a|b;                       //k中为0的位置即是下面那一行横放产生的0,必须满足连续偶数个出现
	for(int i=0;i<n;k/=2,i++)        //统计所有n位连续的0
	{ 
		if(k%2==0) ans=!ans;         //当前位是0, 0的奇偶计数器ans累加
        else                         //当前位是1
		{
			if(ans) return false;        //若连续的0奇数个,不合法
			else ans=0;              //否则继续计数(本行可省)
		}
	}
	return !ans;             //若最后剩下的0奇数个,不合法
}
ll dfs(int sta,int cen)              //记忆化搜索,参数为当前层数的状态及当前层数(层从1开始编号)
{
	if(cen==m-1) return judge(sta,0);
	if(dp[sta][cen]) return dp[sta][cen];
	for(int i=0;i<(1<<n);i++)
	{
		if(judge(sta,i)) dp[sta][cen]+=dfs(i,cen+1);
	}
	return dp[sta][cen];
}
int main()
{
	while(scanf("%d%d",&m,&n),m|n)
	{
		memset(dp,0,sizeof(dp));
		if(m*n%2==1) printf("0\n");
		else printf("%lld\n",dfs(0,0));  //(第“0”层状态一定为0)
	}
	return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值