P1364 醫院設定
阿新 • • 發佈:2020-10-23
floyd
用floyd求出任意一個兩個點之間的最短距離,然後算一下以所有節點為根的情況下的距離之和最小值。
複雜度:\(O(n^3)\)
#include<iostream> #include<cstring> using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n; int g[N][N]; int w[N]; int main(){ memset(g, 0x3f, sizeof g); cin >> n; for(int i = 1; i <= n; i ++){ int a, b; cin >> w[i] >> a >> b; g[i][a] = g[i][b] = g[a][i] = g[b][i] = 1; } for(int i = 1; i <= n; i ++) g[i][i] = 0; for(int k = 1; k <= n; k ++) for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) g[i][j] = min(g[i][j], g[i][k] + g[k][j]); int res = INF; for(int i = 1; i <= n; i ++){ int ans = 0; for(int j = 1; j <= n; j ++) if(i != j) ans += g[i][j] * w[j]; res = min(res, ans); } cout << res; return 0; }
樹的重心
需要預處理出:
- 以一個結點為整棵樹的根u(這個可以任選,這裡選1,這個和後面的遞推起點有關)的情況下,以每一個結點v為根的子樹包括的所有人口s[v]
- 將醫院建在1號點時的總距離f(1)
遞推方法:\(f[v] = f[u] - s[v] + s[1] - s[v];\),f[k]表示把醫院建在k的總距離,s[k]表示包括k在內的以k為根的子樹。
解釋:
v為u的出邊所到達的點,現在已知將醫院建在u時的總距離f[u], 既然v為u的出邊,那麼以v為根的子樹(包括v)本來是走到u的而現在只需要到v,那麼就先需要將f[u]減去s[v], 而除去v的子樹的所有節點(包括v)原本是要走到u的,而現在需要走到v,那麼f[u]還需要加上s[1] (總人口) - s[v]
複雜度:\(O(n)\)
#include<iostream> #include<cstring> #include<queue> using namespace std; const int N = 110, INF = 0x3f3f3f3f; struct Node{ int l, r; int w; }tr[N]; int n; int s[N]; int f[N]; int level[N]; int dfs_1(int u){ if(u == 0) return 0; s[u] += dfs_1(tr[u].l) + dfs_1(tr[u].r) + tr[u].w; return s[u]; } void dfs_2(int u){ int l = tr[u].l, r = tr[u].r; if(l){ f[l] = f[u] - s[l] + s[1] - s[l]; dfs_2(l); } if(r){ f[r] = f[u] - s[r] + s[1] - s[r]; dfs_2(r); } } int main(){ cin >> n; for(int i = 1; i <= n; i ++){ int l, r, w; cin >> w >> l >> r; tr[i] = {l, r, w}; } dfs_1(1); memset(f, 0x3f, sizeof f); queue<int> q; q.push(1); level[1] = 0; f[1] = 0; while(q.size()){ int h = q.front(); q.pop(); int l = tr[h].l, r = tr[h].r; level[l] = level[r] = ++ level[h]; if(l) f[1] += tr[l].w * level[l], q.push(l); if(r) f[1] += tr[r].w * level[r], q.push(r); } dfs_2(1); int res = INF; for(int i = 1; i <= n; i ++) res = min(res, f[i]); cout << res; return 0; }