luoguP5074 Eat the Trees
https://www.luogu.org/problemnew/show/P5074
插頭 $ dp $ 入門題
如果你還不會插頭 $ dp $ 請右轉 洛谷插頭dp題解
雖然是入門題但還是逃不過分類討論的魔爪
這裡採用了括號序列的方法
$ left $ 表示左插頭的狀態,$ up $ 表示右插頭的狀態
情況 1:這個格子不能放線
只有在 $ left == 0 $ && $ up == 0 $ 的時候才能轉移
情況 2:$ left == 0 $ && $ up == 0 $
因為每個格子一定要放線,所以要給這個格子加一個下插頭和一個右插頭
情況 3:$ left == 0 $ && $ up != 0 $
有一個上插頭下來,沒有左插頭,為了讓這個插頭形成迴路,我們給這個插頭加一個下插頭或者右插頭
情況 4:$ left != 0 $ && $ up == 0 $
跟情況 3 類似,這裡不再贅述
情況 5:$ left == 1 $ && $ up == 1 $
一個左插頭和一個上插頭都是左括號,要到右邊去找到一個右括號來匹配,並且將右括號變成左括號
個人認為這是最難理解的一種情況,強烈建議大家自己去畫個圖
情況 6:$ left == 2 $ && $ up == 2 $
根情況 5 類似,只不過變成了到左邊找左括號匹配
情況 7:$ left == 2 $ && $ up == 1 $
相當於左插頭和上插頭抵消了,將這兩個插頭直接去掉後,左插頭本來有一個左括號和它匹配,上插頭有一個右括號和它匹配,仍然滿足括號序列的性質
情況 8:$ left == 1 $ && $ up == 2 $
形成了一個迴路,直接連線即可
當然,如果這個迴路是最後一個迴路的話要統計答案,判斷是否是最後一個迴路只需判斷這個格子是不是最後一個可以放線的點
最後這個題目有一個小坑,就是當整張地圖都不能放線的時候還是算有一種情況的
下面是簡(sang)單(xin)明(bing)了(kuang)的程式碼
#include <bits/stdc++.h> #define Fast_cin ios::sync_with_stdio(false), cin.tie(); #define For(i, a, b) for(register int i = a; i <= b; i++) #define Forr(i, a, b) for(register int i = a; i >= b; i--) #define DEBUG(x) cerr << "DEBUG" << x << " >>> " << endl; using namespace std; typedef unsigned long long ull; typedef long long ll; template <typename _T> inline void read(_T &f) { f = 0; _T fu = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') fu = -1; c = getchar(); } while(c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); } f *= fu; } template <typename T> void print(T x) { if(x < 0) putchar('-'), x = -x; if(x < 10) putchar(x + 48); else print(x / 10), putchar(x % 10 + 48); } template <typename T> void print(T x, char t) { print(x); putchar(t); } const int mod = 5801, N = mod + 100; ll f[2][N], ans; int a[15][15], tot[2], v[2][N], nxt[N], head[N], bin[15]; int T, n, m, now, end1, end2; void ins(int zt, ll val) { int u = zt % mod; for(register int i = head[u]; i; i = nxt[i]) if(v[now][i] == zt) { f[now][i] += val; return; } nxt[++tot[now]] = head[u]; head[u] = tot[now]; f[now][tot[now]] = val; v[now][tot[now]] = zt; } void sol() { tot[now] = 1; f[now][1] = 1; v[now][1] = 0; for(register int i = 1; i <= n; i++) { for(register int j = 1; j <= tot[now]; j++) v[now][j] <<= 2; for(register int j = 1; j <= m; j++) { now ^= 1; memset(head, 0, sizeof(head)); tot[now] = 0; for(register int k = 1; k <= tot[now ^ 1]; k++) { int zt = v[now ^ 1][k]; ll val = f[now ^ 1][k]; int t1 = (zt >> ((j << 1) - 2)) & 3, t2 = (zt >> (j << 1)) & 3; if(a[i][j] == 0) { if(t1 == 0 && t2 == 0) ins(zt, val); } else if(t1 == 0 && t2 == 0) { if(a[i][j + 1] && a[i + 1][j]) ins(zt ^ bin[j - 1] ^ (bin[j] << 1), val); } else if(t1 == 0 && t2 != 0) { if(a[i][j + 1]) ins(zt, val); if(a[i + 1][j]) ins(zt ^ (bin[j] * t2) ^ (bin[j - 1] * t2), val); } else if(t1 != 0 && t2 == 0) { if(a[i + 1][j]) ins(zt, val); if(a[i][j + 1]) ins(zt ^ (bin[j] * t1) ^ (bin[j - 1] * t1), val); } else if(t1 == 1 && t2 == 1) { int nowv = 1; for(register int t = j + 1; t < m; t++) { int t3 = (zt >> (t << 1)) & 3; if(t3 == 1) nowv++; if(t3 == 2) nowv--; if(!nowv) { ins(zt ^ bin[j - 1] ^ bin[j] ^ (bin[t] * 3), val); break; } } } else if(t1 == 2 && t2 == 2) { int nowv = 1; for(register int t = j - 2; t > 0; t--) { int t3 = (zt >> (t << 1)) & 3; if(t3 == 1) nowv--; if(t3 == 2) nowv++; if(!nowv) { ins(zt ^ (bin[j - 1] << 1) ^ (bin[j] << 1) ^ (bin[t] * 3), val); break; } } } else if(t1 == 2 && t2 == 1) { ins(zt ^ (bin[j - 1] << 1) ^ bin[j], val); } else if(i == end1 && j == end2) { ans += val; } else ins(zt ^ bin[j - 1] ^ (bin[j] << 1), val); } } } } int main() { read(T); bin[0] = 1; for(register int i = 1; i <= 11; i++) bin[i] = bin[i - 1] << 2; for(register int Case = 1; Case <= T; Case++) { memset(a, 0, sizeof(a)); read(n); read(m); ans = 0; end1 = end2 = 0; for(register int i = 1; i <= n; i++) { for(register int j = 1; j <= m; j++) { read(a[i][j]); if(a[i][j]) end1 = i, end2 = j; } } if(!end1) { print(1, '\n'); continue; } sol(); print(ans, '\n'); } return 0; }