铺砖块

写了一个多小时才给它写出来...(呜呜呜

首先,因为一块砖占两个格,所以总格数是单数的话就怎么都铺不满。直接输出 \(0\) 即可。

然后考虑状压 dp 。设 \(f[i][j]\) 表示前 \(i\) 行都放满且对第 \(i+1\) 行影响为 \(j\) 时的方案总数。

设数组 \(f1[j],f2[j]\) 表示第 \(j\) 种情况由状态 \(f1[j]\) 可以转移到 \(f2[j]\) 的状态。

则状态转移方程为 f[i][f2[j]]+=f[i-1][f1[j]];

最后再考虑怎么预处理。可以分三种情况讨论:

  • 如果上一行对当前格子的影响为 \(1\) 也就是上面已经有砖块了,那么这里就不放,且对下一行的影响为 \(0\)

  • 如果上一行对当前格子的影响为 \(0\) ,那么我们可以选择竖着放一块,对下一行的影响为 \(1\)

  • 对于上种情况也可以横着放一个,对下一行没有影响,但记得 \(p\) 要一次性加上 \(2\) 。(刚才写的时候这里踩坑了qaq)

#include<bits/stdc++.h>
using namespace std;
int f[12][1<<12],f1[20001],f2[20001];
int m,n,cnt;
void dfs(int p,int st,int ed)
{
	if(p==m)
	{
		cnt++;
		f1[cnt]=st;f2[cnt]=ed;
		return;
	}
	if(p>m) return;
	dfs(p+1,(st<<1)+1,(ed<<1));
	dfs(p+1,(st<<1),(ed<<1)+1);
	dfs(p+2,(st<<2),(ed<<2));
	return;
}
int main()
{
	while(scanf("%d%d",&n,&m))
	{
		if(n==0&&m==0) break;
		cnt=0;
		memset(f,0,sizeof(f));
		dfs(0,0,0);
		f[0][0]=1;
		if(m*n%2)
		{
			cout<<"0"<<endl;
			continue;
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=cnt;j++)
			{
				f[i][f2[j]]+=f[i-1][f1[j]];
			}
		}
		cout<<f[n][0]<<endl;
	}
	return 0;
}
posted @ 2020-12-18 20:49  樱雪喵  阅读(86)  评论(0编辑  收藏  举报