骨牌铺方格的3种做法

应对题目:用1*2的骨牌铺满n*m的矩形总共有多种方法


方法1:状压DP:

复杂度:n*m*(2^m)

解题过程:
        1、为了提高效率,我们规定m=<n,如果n>m就交换
        2、以单个格子作为每个阶段,递推方向为从左上到右下,每个阶段储存前m个方块的状态,1表示已覆盖,0表示未覆盖
        3、阶段决策是:"以当前格子为右下角,要不要放骨牌以及放哪种骨牌",答案有3种:不放骨牌、放竖骨牌、放横骨牌,其中①不放骨牌要求上面的格子必须被覆盖,即k&(1<<(m-1))==1②放竖骨牌要求上面的格子必须没有被覆盖且且不能是第一行,即i!=1 && (k&(1<<(m-1)))==0③放横骨牌要求上面的格子必须被覆盖且左边的格子必须没有被覆盖还不能是第一列,即j!=1 && (k&(1<<(m-1)))!=0 && (k&1)==0

缺点:复杂度略高


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n, m, cur;
long long dp[2][1<<15];		/*1表示当前位置已被填上,0则相反*/
int main(void)
{
	int i, j, k;
	while(scanf("%d%d", &n, &m), n!=0 || m!=0)
	{
		for(i=0;i<=(1<<m)-1;i++)
			dp[0][i] = 1;		/*边界处理,所有dp[0][j]初始化为1*/
		if(m>n)
			swap(n, m);		/*交换行列,使列尽可能窄*/
		cur = 0;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=m;j++)
			{
				if(cur==0)		/*滚动数组*/
					cur = 1;
				else
					cur = 0; 
				memset(dp[cur], 0, sizeof(dp[cur]));
				for(k=0;k<=(1<<m)-1;k++)		//	枚举上个阶段的状态(00000000…(m个0)→11111111…(m个1))
				{
					/*第一种放法:不放    条件:二进制k最左边为1,即k&(1<<(m-1))==1*/
					if(k&(1<<(m-1)))	
						dp[cur][(k<<1)^(1<<m)] += dp[1-cur][k];	
								//(k<<1)^(1<<m)为新状态,所有1和0往左移1位并弃掉最高位的1,因为不放所以不用+1*/
					/*第二种放法:竖放    条件:二进制k最左边为0且当前不是第一行,即k&(1<<(m-1))==0 && i!=1 */
					if(i!=1 && (k&(1<<(m-1)))==0)
						dp[cur][(k<<1)+1] += dp[1-cur][k];	
								//(k<<1)+1为新状态,所有1和0往左移一位并+1, 因为最高位是0没必要弃掉
					/*第三种放法:横放    条件:二进制k最左边为1,最右边为0,且当前不是第一列,即(k&(1<<(m-1)))!=0 && (k&1)==0 && j!=1)*/
					if(j!=1 && (k&(1<<(m-1)))!=0 && (k&1)==0)
						dp[cur][(k<<1)^(1<<m)+3] += dp[1-cur][k];     
								//(k<<1)^(1<<m)+3为新状态,所有1和0往左移一位并+3,弃掉最高位的1
				}
			}
		}
		printf("%lld\n", dp[cur][(1<<m)-1]);	//全部填满的状态
	}
	return 0;
}



方法2:公式:

https://en.wikipedia.org/wiki/Domino_tiling#CITEREFKasteleyn1961

复杂度:n*m


缺点:无法取模


#include<stdio.h>
#include<math.h>
#define PI acos(-1.0)
int main(void)
{
	double n, m, sum;
	int i, j;
	while(scanf("%lf%lf", &n, &m), n!=0 || m!=0)
	{
		sum = 1;
		for(i=1;i<=(m+1)/2;i++)
		{
			for(j=1;j<=(n+1)/2;j++)
				sum *= 4*cos(PI*i/(m+1))*cos(PI*i/(m+1))+4*cos(PI*j/(n+1))*cos(PI*j/(n+1));
		}
		printf("%.0f\n", sum);
	}
	return 0;
}


方法3:矩阵快速幂:

详见http://blog.csdn.net/jaihk662/article/details/77750353


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值