铺砖块

题目描述

现有n*m的一块地板,需要用1*2的砖块去铺满,中间不能留有空隙。问这样方案有多少种 

 

输入

输入n,m(1<=n, m<=11) 
有多组输入数据,以m=n=0结束 

 

输出

输出铺砖块的方案数

 

样例输入

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

样例输出

1
0
1
2
3
5
144
51205

 

典型的状压dp

搜索预处理出转移

注意long long

#include<cstdio>
#define ll long long
using namespace std;
inline int read()
{
	int ret=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9')
		ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
	return ret;
}

int n,m,num;
const int N=12,S=15000;
int s1[S],s2[S];
ll f[N][S];

void dfs(int t,int a,int b)
{
	if(t==m) 
	{
		s1[++num]=a,s2[num]=b;
		return;
	}
	if(t>m) return;
	dfs(t+1,a<<1,(b<<1)+1);
	dfs(t+1,(a<<1)+1,b<<1);
	dfs(t+2,(a<<2)+3,(b<<2)+3);
}
int main()
{
	n=read(),m=read();
	while(n&&m)
	{
		num=0;
		for(int i=0;i<=n;i++)
			for(int j=0;j<(1<<m);j++) f[i][j]=0;
		dfs(0,0,0);
		f[0][(1<<m)-1]=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=num;j++)
				f[i][s2[j]]+=f[i-1][s1[j]];
		printf("%lld\n",f[n][(1<<m)-1]);
		n=read(),m=read();
	}
	return 0;
}

这是由行转移,可以改成由格子转移

          
          
    ******
****&     
          
          
          
          
          
          

当你要决策&的时候,你影响的只有*

像不像状压?

所以可以状压前m个格子                                                                                            

当你往后移的时候前面的格子也要移,那就要处理了

   (保证在非(*和&和*&下面的格子)全部为1(1为有,2为空))

枚举格子O(n*m),状压前m个O(2^m)

所以时间为O(n*m*2^m)

一点都不优秀在这道题中,但是再有些题目中却如有神助

#include<cstdio>
#define ll long long
using namespace std;
inline int read()
{
	int ret=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9')
		ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
	return ret;
}

int n,m;
const int N=13,ss=2500;
ll ans;
ll f[N][N][ss];

void dfs(int t,int S)
{
	if(t==m)
	{
		ans+=f[n+1][1][S]; return;
	}
	if(t>m) return;
	dfs(t+1,(S<<1)+1);
	dfs(t+2,S<<2);
}
int main()
{
	n=read(),m=read();
	while(n&&m)
	{
		for(int i=1;i<=n+1;i++)
			for(int j=1;j<=m;j++)
				for(int S=0;S<(1<<m);S++) f[i][j][S]=0;
		f[1][1][(1<<m)-1]=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				for(int S=0;S<(1<<m);S++)
					if(j<m) 
					{
						if((1<<(m-1))&S) f[i][j+1][(S-(1<<(m-1)))<<1]+=f[i][j][S];
							else 
							{
								f[i][j+1][(S<<1)|1]+=f[i][j][S];
								if((1<<(m-2)&S)==0) f[i][j+1][(S|(1<<(m-2)))<<1]+=f[i][j][S];
							}
					} else
					{
						if((1<<(m-1))&S) f[i+1][1][(S-(1<<(m-1)))<<1]+=f[i][j][S];
							else f[i+1][1][(S<<1)|1]+=f[i][j][S];
					}
		ans=0; 
		dfs(0,0);
		printf("%lld\n",ans);
		n=read(),m=read();
	}
	return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值