1. 程式人生 > >hdu1693 Eat the Trees 【插頭dp】

hdu1693 Eat the Trees 【插頭dp】

分享圖片 技術 esp urn case line 狀態 連通性 cas

題目鏈接

hdu1693

題解

插頭\(dp\)

特點:範圍小,網格圖,連通性
輪廓線:已決策點和未決策點的分界線
插頭:存在於網格之間,表示著網格建的信息,此題中表示兩個網格間是否連邊
狀態表示:當前點\((i,j)\)和輪廓線上\(m + 1\)個插頭的狀態

狀態轉移:
技術分享圖片
我們用\(f[i][j][s]\)表示如上的狀態,最後一次決策點為\((i,j)\),輪廓線上插頭狀態為\(s\)的方案數
比如上圖\(s = 1101001\)

之後我們擴展新的點,枚舉它插頭的狀態進行轉移
技術分享圖片
在本題中,要使最終形成若幹回路,每個點度數必須為\(2\),所以我們擴展點的時候記錄它已有的插頭數,然後剩余的插頭數就可以唯一確定

然後就可以\(O(nm2^m)\)過了這道插頭\(dp\)入門題

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s)) #define cp pair<int,int> #define LL long long int using namespace std; const int maxn = 12,maxm = 100005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while
(c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int n,m,S[maxn][maxn]; LL f[maxn][maxn][1 << maxn]; void work(int C){ cls(f); if (!S[1][1]) f[1][1][0] = 1; else { if (S[1][2] == 0 || S[2][1] == 0){ printf("Case %d: There are 0 ways to eat the trees.\n",C); return; } f[1][1][3] = 1; } int maxv = (1 << m + 1) - 1,cnt,e,t; for (int i = 1; i <= n; i++){ for (int j = 1; j <= m; j++){ if (i == n && j == m) break; for (int s = 0; s <= maxv; s++){ if (!f[i][j][s]) continue; if (j == m){ if (s & 1){ if (i + 2 <= n && S[i + 2][1]) f[i + 1][1][(s >> 1) << 2 | 1] += f[i][j][s]; if (S[i + 1][2]) f[i + 1][1][(s >> 1) << 2 | 2] += f[i][j][s]; } else { if (!S[i + 1][1]) f[i + 1][1][(s >> 1) << 2] += f[i][j][s]; else { if (i + 2 > n || !S[i + 2][1] || !S[i + 1][2]) continue; f[i + 1][1][(s >> 1) << 2 | 3] += f[i][j][s]; } } } else { cnt = ((s >> j) & 1) + ((s >> j + 1) & 1); t = (s >> j) & 3; e = s ^ (t << j); if (cnt && !S[i][j + 1]) continue; if (cnt == 2) f[i][j + 1][e] += f[i][j][s]; else if (cnt == 1){ if (i + 1 <= n && S[i + 1][j + 1]) f[i][j + 1][e | (1 << j)] += f[i][j][s]; if (j + 2 <= m && S[i][j + 2]) f[i][j + 1][e | (1 << j + 1)] += f[i][j][s]; } else { if (!S[i][j + 1]) f[i][j + 1][e] += f[i][j][s]; else { if (i + 1 > n || j + 2 > m || !S[i + 1][j + 1] || !S[i][j + 2]) continue; f[i][j + 1][e | (3 << j)] += f[i][j][s]; } } } } } } LL ans = 0; for (int s = 0; s <= maxv; s++) ans += f[n][m][s]; printf("Case %d: There are %lld ways to eat the trees.\n",C,ans); } int main(){ int T = read(); REP(t,T){ n = read(); m = read(); REP(i,n) REP(j,m) S[i][j] = read(); if (n == 1 || m == 1){ if (!S[n][m]) printf("Case %d: There are 1 ways to eat the trees.\n",t); else printf("Case %d: There are 0 ways to eat the trees.\n",t); continue; } work(t); } return 0; }

hdu1693 Eat the Trees 【插頭dp】