题意:
n * m的矩阵,用1 * 2和2 * 1的砖快密铺,问多少种方法:
题解:
方法1:
我们现在规定砖头的竖放的上部分为1,砖头的横放或者是竖放的下部分为0
我们每两层进行分析,分析01的关系
我们设上层为k,下层为j
因为竖砖上为1下为0,所以上下两层&的结果必然都是0,j&k = = 0,
且j|k中连续的0均为偶数,因为竖砖和横砖的组合只有两种情况如下图:
蓝色字为两行或的结果,或的结果中0只能是连续偶数个出现,不可能连续奇数个出现
方法二:
我们用01表示这个地方放不放砖
第i行只与第i-1行相关
枚举第i-1行的每个状态,就可以推出第i行的状态
如果第i-1行的某处没放,那么第i行这个位置必须放竖砖,所以对上一行状态按位取反之后的1的位置就是放置了竖方块的状态。
然后枚举这行其他点的位置,记录答案
这个思路很常规,但也很妙
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, m;
int const N = 1e4 + 10;
LL f[20][N];
int st[N];
vector<int> state[N];
int main() {
while (cin >> n >> m && n && m) {
// 初始化
memset(f, 0, sizeof f);
f[0][0] = 1;
for (int i = 0; i < 1 << n; ++i) state[i].clear();
// 预处理st
for (int i = 0; i < 1 << n; i ++ ) {
int cnt = 0;
st[i] = true;
for (int j = 0; j < n; j ++ )
if (i >> j & 1) {//如果第j位是1 ,统计之前连续0的数量奇偶性
if (cnt & 1) st[i] = false;
cnt = 0;
}
else cnt ++ ;//如果是第j位是0,记录连续0的数量
if (cnt & 1) st[i] = false;
}
// 预处理state
for (int i = 0; i < 1 << n; ++i) {
for (int j = 0; j < 1 << n; ++j) {
if ((i & j) == 0 && st[i | j]) state[i].push_back(j);
}
}
// dp转移
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < 1 << n; ++j) {
for (int k = 0; k < state[j].size(); ++k) { // 获得所有的合法方案
f[i][j] += f[i - 1][state[j][k]];
}
}
}
printf("%lld\n", f[m][0]);
}
return 0;
}
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
int n,m;
ll add;
ll dp[2][1<<12];
void dfs(int i,int s,int cur)
{
if(cur==m) {dp[i][s]+=add;return;}
dfs(i,s,cur+1);
if(cur<m-1 && !(s&(1<<cur)) && !(s&1<<(cur+1)) )
dfs(i,s|(1<<cur)|1<<(cur+1),cur+2);
}
int main()
{
while(scanf("%d%d",&n,&m),n+m)
{
if(n*m%2) {printf("0\n");continue;}
int rt=(1<<m)-1;
add=1;
memset(dp,0,sizeof(dp));
dfs(0,0,0);
for(int i=1;i<n;i++)
for(int j=0;j<=rt;j++)
if(dp[(i-1)%2][j])
{
add=dp[(i-1)%2][j];
dfs(i%2,~j&rt,0);
}
printf("%I64d\n",dp[(n-1)%2][rt]);
}
return 0;
}