多米诺骨牌问题,状态压缩dp

转:

链接:https://blog.csdn.net/shiwei408/article/details/8821853

https://blog.csdn.net/u013480600/article/details/19499899

题目描述:用1*2 的矩形通过组合拼成大矩形,求拼成指定的大矩形有几种拼法。

首先 我们先求用1*2 的矩形拼成 n*m的矩形有多少种拼法

当n*m为奇数时,一定是不会拼出来的,因为想要拼出来就需要整数倍的小矩形数目。

为了加速算法,要把m,n中小的那个当做列

分两个步骤:1) 先求出相邻两行的转化关系 

            2) 通过相邻两行的转化关系算出经过n次转化有几种方法能拼成n*m的矩阵

1) 状态标记 横放和竖放的下一个均为1,竖放的上一个和不放置为0 ,每行可以转化为1个2进制数。当这一行访问结束时,就会得到上一行状态,和该行状态,因为所有情况都是我们设置的,所以pre状态一定会转化为now状态

对于每一个位置,我们有三种放置方法:1. 竖直放置2. 水平放置3. 不放置
d为当前列号 ,初始化d, now, pre都为0。now为当前行,pre为当前行的上一行
1. d = d + 1, now << 1 | 1, pre << 1;   // 竖直放置,当前行该列为1,上一行该列置为为0
2. d = d + 2, now << 2 | 3, pre<< 2 | 3;  // 横放 都为11
3. d = d + 1, now << 1, pre<< 1 | 1;    // 上一行该列置为1,不能竖放,不放置的状态
因为转移状态有很多种,所以用dfs去枚举各种可行的状态。

下面是dfs实现:

 

2) 求出来相邻两行之间的状态转化,下面就要求经过n次转化后最后状态为(1<<m - 1) 的总数,对于n比较小(可用矩阵存储) 的时候可用

当n很大的时候,就不能用上述的方法算了,这个时候矩阵的优势就体现出来了

同样是求经过n次转化,从初始态到终态有几种转化法

建立矩阵的方法很简单,矩阵的大小为(1<<m-1) 如过pre状态能到达状态now,那么++matrix[pre][now]; 然后求此矩阵的n次幂即可

1、poj2411 题意:给定一个长宽小于等于11的矩形,问用1×2的小矩形填满,有多少种方法。

 
  1.  
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define LL long long
    using namespace std;
    const int maxn=13;
    int w,h,tan;
    LL dp[13][2100];//1<<11
    int path[14000][2];//11*(1<<11)
    void dfs(int l,int now,int pre)
    {
    	if(l>w)return;
    	if(l==w)
    	{
    		path[tan][0]=pre;
    		path[tan++][1]=now;
    		return ;	
    	}
    	dfs(l+2,(now<<2)|3,(pre<<2)|3);
    	dfs(l+1,(now<<1)|1,pre<<1);
    	dfs(l+1,now<<1,(pre<<1)|1);
    }
    int main()
    {
    	while(~scanf("%d%d",&h,&w))
    	{
    		if(!h&&!w)break;
    		if(h<w){int t=h;h=w;w=t;}
    		tan=0;
    		dfs(0,0,0);
    		memset(dp,0,sizeof(dp));
    		dp[0][(1<<w)-1]=1;
    		for(int i=0;i<h;++i)
    		{
    			for(int j=0;j<tan;++j)
    			{
    				dp[i+1][path[j][1]]+=dp[i][path[j][0]];
    			}
    		}
    		printf("%lld\n",dp[h][(1<<w)-1]);
    	}
    	return 0;
    }
    

    2、poj2663 题意:给定1*2的小矩形,去拼接一个3*n(n<30)的矩形,问有多少种方案。

    3、poj3420 题意:给定1*2的小矩形,去拼接一个4*n(n<10^9)的矩形,问有多少种方案。N这么大递推肯定是不行了,所以我们要用矩阵快速幂进行加速,这个转移矩阵如何构造呢,我们可以直接用pre状态转移到now状态采用邻接矩阵的方式表述就好了。这个矩阵的大小为at[16][16],at[15][15]就是最后要求的状态

#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
struct mat{
	LL at[16][16];
};
mat dat;
int n,mod;
void dfs(int l,int now,int pre)
{
	if(l>4)return;
	if(l==4)
	{
		++dat.at[pre][now];
		return;
	}
	dfs(l+1,(now<<1)|1,(pre<<1));
	dfs(l+1,(now<<1),(pre<<1)|1);
	dfs(l+2,(now<<2)|3,(pre<<2)|3);
}
mat mul(mat a,mat b)
{
	mat c;
	memset(c.at,0,sizeof(c.at));
	for(int i=0;i<16;++i)
	{
		for(int k=0;k<16;++k)
		{
			if(a.at[i][k])
			{
				for(int j=0;j<16;++j)
				{
					c.at[i][j]+=a.at[i][k]*b.at[k][j];
					if(c.at[i][j]>=mod){c.at[i][j]%=mod;}
				}
			}
		}
	}
	return c;
}
mat expo(mat a,int k)
{
	if(k==1)return a;
	mat e;
	memset(e.at,0,sizeof(e.at));
	for(int i=0;i<16;++i){e.at[i][i]=1;}
	if(k==0)return e;
	while(k)
	{
		if(k&1)e=mul(a,e);
		a=mul(a,a);
		k>>=1;
	}
	return e;
}
int main()
{
	memset(dat.at,0,sizeof(dat.at));
	dfs(0,0,0);
	while(~scanf("%d%d",&n,&mod))
	{
		if(!n&&!mod)break;
		if(mod==1){printf("0\n");continue;}
		mat ans=expo(dat,n);
		printf("%lld\n",ans.at[15][15]);	
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值