P1364 醫院設定 樹形DP + 帶權樹的重心
阿新 • • 發佈:2020-10-20
傳送門
題目描述
設有一棵二叉樹,如圖:
其中,圈中的數字表示結點中居民的人口。圈邊上數字表示結點編號,現在要求在某個結點上建立一個醫院,使所有居民所走的路程之和為最小,同時約定,相鄰接點之間的距離為 11。如上圖中,若醫院建在1 處,則距離和 =4+12+2\times20+2\times40=136=4+12+2×20+2×40=136;若醫院建在 33 處,則距離和 =4\times2+13+20+40=81=4×2+13+20+40=81。
輸入描述
第一行一個整數 nn,表示樹的結點數。 接下來的 nn 行每行描述了一個結點的狀況,包含三個整數 w, u, vw,u,v,其中 ww 為居民人口數,uu 為左連結(為 00 表示無連結),vv 為右連結(為 00 表示無連結)。
輸出描述
一個整數,表示最小距離和。
分析
這道題和P3478 [POI2008]STA-Station有點類似,大致思路都是從樹的深度下手,找出狀態轉移方程
首先這道題的資料比較有意思,比較直接的方法就是用Floyd求出兩點間的距離,然後列舉每一個點即可,時間複雜度為O(n
2
)
還有一種O(n)的做法就是先求出以1為根結點時的答案,然後不斷往下轉換根節點,由於子樹的深度 -1,所以對答案的影響是 - Size[j],上半部分的深度 +1,所以對答案的影響是 Size[1] - Size[j],所以狀態轉移方程為:f[j] = f[u] +Size[1] - Size[j] * 2;
程式碼
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const int N = 110,M = 2 * N; int h[N],ne[M],e[M],idx; int d[N]; int w[N]; int Size[N]; int f[N]; int n; int ans = 0x3f3f3f3f; void add(int x,int y){ ne[idx] = h[x],e[idx] = y,h[x] = idx++; } void dfs(int u,int fa){ d[u] = d[fa] + 1; Size[u] = w[u]; for(int i = h[u];~i;i = ne[i]){ int j = e[i]; if(j == fa) continue; dfs(j,u); Size[u] += Size[j]; } f[1] += d[u] * w[u]; } void dp(int u,int fa){ for(int i = h[u];~i;i = ne[i]){ int j = e[i]; if(j == fa) continue; f[j] = f[u] + Size[1] - Size[j] * 2; dp(j,u); } ans = min(ans,f[u]); } int main(){ scanf("%d",&n); memset(h,-1,sizeof h); for(int i = 1;i <= n;i++){ int x,y; scanf("%d%d%d",&w[i],&x,&y); if(x) add(i,x),add(x,i); if(y) add(i,y),add(y,i); } d[0] = -1; dfs(1,0); dp(1,0); printf("%d\n",ans); return 0; }