NC51189 Mondriaan’s Dream
题意:
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();// 预处理stfor (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;}// 预处理statefor (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;
}