Mondriaan's Dream题解

题目

求用1*2的牌铺满n*m大小的棋盘的方案数。

思路

关键是设计牌状态,尤其是竖着的。

如果令竖着的骨牌上面为1。

这样两个状态不冲突的条件是某一位不能同时为1且或运算后不能有连续奇数个0。

最终第n行应该是全0。

考虑第一行, 只要其不含奇数个0, 就合法,为了方便, 设计第0行的0方案数为1, 这样按照策略转移第一行的全部合法状态都会变成1. 当然也可以特殊处理第一行。

最终答案为f[n][0];

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>

using namespace std;

int n, m;
long long f[12][1<<11];
int valid[1<<11];

int check(int S) {
	int k = 0;
	while(k < m) {
		if(S&1) S >>= 1, k++;
		else if(!(S&1) && !(S&2) && k+2 <= m) S >>= 2, k += 2;
		else return 1;
	}
	return 0;
}

int main() {
	while(scanf("%d%d", &n, &m) && n && m) {
		memset(f, 0, sizeof(f));
		memset(valid, 0, sizeof(valid));
		
		for(int S = 0; S < (1<<m); S++) {
			if(check(S)) continue;
			valid[S] = 1;
		}
		
		//We can also deal the first line and start at i = 2
//		for(int S = 0; S < (1<<m); S++) {
//			if(check(S)) continue;
//			valid[S] = 1;
//			f[1][S] = 1;
//		}
		
		
		f[0][0] = 1;
		int now = 1;
		for(int i = 1; i <= n; i++, now ^= 1) {
			memset(f[now], 0, sizeof(f[now]));
			for(int v = 0; v < (1<<m); v++) {
				for(int u = 0; u < (1<<m); u++) {
					if((u&v) || !valid[u|v]) continue;
					f[now][v] += f[now^1][u];
				}
			}
		}

		
		cout << f[now^1][0] << '
';
	}
	return 0;
}

如果令竖着的骨牌下面为1. 状态冲突条件不变。

考虑第一行只能全为0, 设计其方案数为1. 这样第二行合法的便全部可以转移呈1.

处理完后, 最后一行需要遍历一下所有状态, 只要不含奇数个0, 就合法,累加到答案中。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>

using namespace std;

int n, m;
long long f[12][1<<11];
int valid[1<<11];

int check(int S) {
	int k = 0;
	while(k < m) {
		if(S&1) S >>= 1, k++;
		else if(!(S&1) && !(S&2) && k+2 <= m) S >>= 2, k += 2;
		else return 1;
	}
	return 0;
}

int main() {
	while(scanf("%d%d", &n, &m) && n && m) {
		memset(f, 0, sizeof(f));
		memset(valid, 0, sizeof(valid));
		
		for(int S = 0; S < (1<<m); S++) {
			if(check(S)) continue;
			valid[S] = 1;
		}
		
		f[1][0] = 1;
		int now = 0;
		for(int i = 2; i <= n; i++, now ^= 1) {
			memset(f[now], 0, sizeof(f[now]));
			for(int v = 0; v < (1<<m); v++) {
				for(int u = 0; u < (1<<m); u++) {
					if((u&v) || !valid[u|v]) continue;
					f[now][v] += f[now^1][u];
				}
			}
		}

		long long ans = 0;
		for(int S = 0; S < (1<<m); S++) if(valid[S]) ans += f[now^1][S]; 
		cout << ans << '
';
	}
	return 0;
}

关键

竖着的骨牌的处理不同, 决策不同。

动态规划状态的设计和转移的策略。 需要考虑完备。

预处理合法状态。

原文地址:https://www.cnblogs.com/Maktub-blog/p/15064277.html