题意:给一个n*m的棋盘用1*2的骨牌铺满,有多少种方法。
思路:lrj训练指南的原题,讲的很清楚,以当前格子为右下角,在决定是否要放的时候,以当前格子往前数m格,形成一个状态,当然假设这些格子之前的都已经铺满了,那么只有不放,左放,上放三种状态,如果上面格子没放,那么久一定要上放,不然之后无法再铺满上面的格子。
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
const int maxn = (1 << 11);
using namespace std;
ll dp[110][maxn];
int n, m, tal;
int a[15];
ll dfs(int x, int y, int S) {
if(y == m) return dfs(x + 1, 0, S);
ll &ans = dp[x * m + y][S];
if(ans >= 0) return ans;
int s = S;
for(int i = m - 1; i >= 0; i--) {
a[i] = S % 2;
S >>= 1;
}
int cc = x * m + y;
ans = 0;
if(x * m + y == tal) {
ll can = 1;
for(int i = 1; i < m - 1; i++)
if(!a[i]) can = 0;
if(m == 1 && a[0]) can = 0;
if(m > 1 && (a[0] ^ a[m - 1]) == 0) can = 0;
return ans = can;
}
if(!a[0]) a[m] = 1;
else a[m] = 0;
int nxt = 0;
for(int i = 1; i <= m; i++) nxt = nxt * 2 + a[i];
ans += dfs(x, y + 1, nxt);
for(int i = m - 1; i >= 0; i--) {
a[i] = s % 2; s >>= 1;
}
if(a[0] && y && !a[m - 1]) {
a[m] = a[m - 1] = 1;
nxt = 0;
for(int i = 1; i <= m; i++) nxt = nxt * 2 + a[i];
ans += dfs(x, y + 1, nxt);
}
return ans;
}
int main() {
while(scanf("%d %d", &n, &m) != EOF) {
if(n < m) swap(n, m);
tal = n * m - 1;
memset(dp, -1, sizeof dp);
ll ans = dfs(0, 0, (1 << m) - 1);
printf("%lld\n", ans);
}
return 0;
}