1. 程式人生 > 其它 >Mondriaan's Dream題解

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] << '\n';
	}
	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 << '\n';
	}
	return 0;
}

關鍵

豎著的骨牌的處理不同, 決策不同。

動態規劃狀態的設計和轉移的策略。 需要考慮完備。

預處理合法狀態。