『动态规划·状态压缩』广场铺砖问题

问题1

有一个n行m列的广场,需要用1*2小砖铺盖,小砖之间互相不能重叠,问有多少种不同的铺法?

题解

f [ i ] [ j ] f[i][j] 表示第 i i 行,状态为 j j 的方案数。在状态 j j 中, 1 1 表示竖着放长方形的上半部分。 0 0 表示其余部分。

显然上一排空缺的部分肯定是要竖着放置的。

就看剩下横着的能不能连续的两个两个放置。

状态转移很显然: f [ i ] [ j ] = f [ i 1 ] [ k ] f[i][j]=\sum f[i-1][k]

关键在于我们应该如何选取合法的状态。

  • 条件1 j   a n d   k = 0 j\ and\ k=0
  • 原因:保证上半部分的 1 1 下面一定有空缺。
  • 条件2 j   o r   k 0. j\ or\ k的二进制中不能有连续奇数个0.
  • 原因 o r or 运算的结果中,要么和前一行和当前行组成竖着的,要么就是当前行和下一行组成竖着的。反正, 0 0 的形态一定要由横躺着的来填补,因此必须要有连续个偶数个 0 0 .

代码如下:

#include <bits/stdc++.h> 

#define int long long

using namespace std;
const int N = 12;

int n, m;
int ok[1<<N], f[N][1<<N];

signed main(void)
{
	freopen("floor.in","r",stdin);
	freopen("floor.out","w",stdout);
	cin >> n >> m;
	for (int i=0;i<(1<<m);++i)
	{
		int j = i;
		bool flag = 0, cnt = 0;
		for (int j=m-1;j>=0;--j)
		    if (i >> j & 1) flag |= cnt, cnt = 0;
		    else cnt ^= 1;
		ok[i] = (flag|cnt)^1;
	}
	f[0][0] = 1;
	for (int i=1;i<=n;++i)
		for (int j=0;j<(1<<m);++j)
		    for (int k=0;k<(1<<m);++k)
		        if ((j & k) == 0 && ok[k|j] == 1)
				    f[i][j] += f[i-1][k];
	cout<< f[n][0] << endl;
	return 0; 
}

问题2

给出n××m(1≤n、m≤9)的方格棋盘,用12 的矩形的骨牌和L 形的(22 的去掉一个角)骨牌不重叠地覆盖,求覆盖满的方案数。

题解

这题比较玄乎,我们需要使用dfs来解决。

我们考虑当前行的状态s1,和上一行的状态s2的关系来转移。显然有 f [ i ] [ s 1 ] = f [ i 1 ] [ s 2 ] f[i][s1]=\sum f[i-1][s2]

问题还是转移合法性。

我们在每一行转移的时候,尝试去枚举每一位,如果对右边有影响,即如果要往右边延伸的话用一个变量标记,在下一次转移的时候特判即可。

b1表示当前列是否被收到了影响,即是否已经被占据了。是为1,否则为0.b2表示前一行。

注意前一行的状态标记状态要反着来,例如要对前一行填充,那么转移的状态就是空的。

在这里插入图片描述

距离是这样的。

代码如下:

#include <bits/stdc++.h>

#define int long long

using namespace std;
const int N = 12;

int n, m, i;
int f[N][1<<N];

void dfs(int k,int s1,int s2,int b1,int b2) 
{
	if (k > m && b1 == 0 && b2 == 0) f[i][s2]+=f[i-1][s1];
	if (k > m) return; 
	dfs(k+1,s1*2+1-b1,s2*2+b2,0,0);
	if (b2 == 0) dfs(k+1,s1*2+1-b1,s2*2+1,0,1);
	if (b2 == 0) dfs(k+1,s1*2+1-b1,s2*2+1,1,1);
	if (b1 == 0) dfs(k+1,s1*2,s2*2+b2,1,1);
	if (b1 == 0 && b2 == 0) dfs(k+1,s1*2,s2*2+1,0,0);
	if (b1 == 0 && b2 == 0) dfs(k+1,s1*2,s2*2+1,1,0);
	if (b1 == 0 && b2 == 0) dfs(k+1,s1*2,s2*2+1,0,1);
	return;
}

signed main(void)
{
	freopen("examsix.in","r",stdin);
	freopen("examsix.out","w",stdout);
	cin>>n>>m;
	f[0][(1<<m)-1] = 1;
	for (i=1;i<=n;++i) dfs(1,0,0,0,0);
	cout<<f[n][(1<<m)-1];
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/94429613