hdu1693 Eat the Trees 【插頭dp】
阿新 • • 發佈:2018-06-16
分享圖片 技術 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】