1. 程式人生 > >poj 2411 Mondriaan's Dream 骨牌鋪放 狀壓dp

poj 2411 Mondriaan's Dream 骨牌鋪放 狀壓dp

bool detail href 位置 pre ret sin tails 等價

題目鏈接

題意

\(1\times 2\)的骨牌鋪滿\(H\times W(H,W\leq 11)\)的網格,問方案數。

思路

參考focus_best.

豎著的骨牌用\(\begin{pmatrix}0\\1\end{pmatrix}\)表示,橫著的骨牌用\(\begin{pmatrix}1&1\end{pmatrix}\)表示。

則對於第\(i\)行,與之相容的第\(i-1\)行的狀態需滿足:

  1. \(i\)行是0的位置,第\(i-1\)行必須是1;
  2. \(i\)行是1的位置,第\(i-1\)行可為1可為0;如果是1則需滿足,這樣的連續的1的個數必為偶數(因為為橫放)。

此外,
最後一行必為全1狀態,

第一行需滿足可以作為第一行的條件:這等價於,第一行的上一行可以表示為全1狀態。

Code

#include <cstdio>
#include <cstring>
#include <iostream>
#define F(i, a, b) for (int i = (a); i < (b); ++i)
#define F2(i, a, b) for (int i = (a); i <= (b); ++i)
#define dF(i, a, b) for (int i = (a); i > (b); --i)
#define dF2(i, a, b) for (int i = (a); i >= (b); --i)
#define maxn 13 #define maxs 2100 using namespace std; typedef long long LL; int h, w; LL dp[maxs][maxn]; bool vis[maxs][maxn], hori[maxn]; bool check(int s1, int s2) { memset(hori, 0, sizeof hori); F(i, 0, w) { if (!(s2&(1<<i))) { if (!(s1&(1<<i))) return
false; } else { if (s1&(1<<i)) hori[i] = true; } } int cont = 0; bool flag = false; F(i, 0, w+1) { if (!hori[i]) { if (cont&1) return false; cont = 0; } else ++cont; } return true; } LL dfs(int state, int row) { if (!row) { if (state==(1<<w)-1) return 1; else return 0; } if (vis[state][row]) return dp[state][row]; vis[state][row] = true; LL temp=0; F(i, 0, 1<<w) { if (check(i, state)) temp += dfs(i, row-1); } return dp[state][row] = temp; } void work() { memset(vis, 0, sizeof vis); memset(dp, 0, sizeof dp); if (h<w) swap(h,w); printf("%lld\n", dfs((1<<w)-1, h)); } int main() { while (scanf("%d%d", &h, &w) != EOF && h && w) work(); return 0; }

poj 2411 Mondriaan's Dream 骨牌鋪放 狀壓dp