Mondriaan's Dream題解
阿新 • • 發佈:2022-04-07
題目
求用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; }
關鍵
豎著的骨牌的處理不同, 決策不同。
動態規劃狀態的設計和轉移的策略。 需要考慮完備。
預處理合法狀態。