问题1
有一个n行m列的广场,需要用1*2小砖铺盖,小砖之间互相不能重叠,问有多少种不同的铺法?
题解
设 表示第 行,状态为 的方案数。在状态 中, 表示竖着放长方形的上半部分。 表示其余部分。
显然上一排空缺的部分肯定是要竖着放置的。
就看剩下横着的能不能连续的两个两个放置。
状态转移很显然:
关键在于我们应该如何选取合法的状态。
- 条件1:
- 原因:保证上半部分的 下面一定有空缺。
- 条件2:
- 原因: 运算的结果中,要么和前一行和当前行组成竖着的,要么就是当前行和下一行组成竖着的。反正, 的形态一定要由横躺着的来填补,因此必须要有连续个偶数个 .
代码如下:
#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的关系来转移。显然有
问题还是转移合法性。
我们在每一行转移的时候,尝试去枚举每一位,如果对右边有影响,即如果要往右边延伸的话用一个变量标记,在下一次转移的时候特判即可。
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;
}