POJ2152 Fire
阿新 • • 發佈:2020-07-24
http://poj.org/problem?id=2152
樹型DP
資料範圍較小,首先,暴力求出兩點間距離
令\(f[i]\)表示以\(i\)為根的子樹全部被消防站覆蓋的最小花費,\(g[i][j]\)表示以\(i\)為根的子樹全部被消防站覆蓋,且\(u\)使用\(i\)的消防站的最小花費
\[f[u]=\min_{1\le i \le n} g[u][i]\\ g[u][i]=w[i]+\min_{v是u的兒子} (g[v][i]-w[i],f[v])\\ g[v][i]-w[i]:v也從屬於i,那麼i站被建了兩次,需要減去重複部分\\ \min_{v是u的兒子} (g[v][i]-w[i],f[v])保證了u的子節點被完全覆蓋,同時u也被覆蓋了,從而保證了以u為根的子樹全部被消防站覆蓋 \]
\(C++ Code:\)
#include<cstdio> #include<iostream> #include<cstring> #define INF 1000000009 #define N 2005 #define RN 1005 using namespace std; int T,x,y,z,n,rt,tot,head[RN],nxt[N],d1[N],d2[N],f[RN],g[RN][RN],dis[RN][RN],w[RN],d[RN]; void Clear() { for (int i=0;i<=RN;f[i]=INF,i++) for (int j=0;j<=RN;j++) g[i][j]=INF; memset(head,0,sizeof head); tot=0; } void add(int x,int y,int z) { tot++; d1[tot]=y,d2[tot]=z; nxt[tot]=head[x],head[x]=tot; } void dfs(int u,int f) { for (int i=head[u];i;i=nxt[i]) { int v=d1[i]; int cost=d2[i]; if (v==f) continue; dis[rt][v]=dis[rt][u]+cost; dfs(v,u); } } void tree_dp(int u,int F) { for (int i=1;i<=n;i++) if (dis[u][i]<=d[u]) g[u][i]=w[i]; else g[u][i]=INF; for (int i=head[u];i;i=nxt[i]) { int v=d1[i]; if (v==F) continue; tree_dp(v,u); } for (int i=head[u];i;i=nxt[i]) { int v=d1[i]; if (v==F) continue; for (int i=1;i<=n;i++) if (dis[u][i]<=d[u]) g[u][i]+=min(f[v],g[v][i]-w[i]); } for (int i=1;i<=n;i++) f[u]=min(f[u],g[u][i]); } int main() { scanf("%d",&T); while (T --> 0) { Clear(); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&w[i]); for (int i=1;i<=n;i++) scanf("%d",&d[i]); for (int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } for (int i=1;i<=n;i++) { dis[i][i]=0; rt=i; dfs(i,0); } tree_dp(1,0); cout << f[1] << endl; } }