Codeforces Gym101667 A. Broadcast Stations 樹形DP
Codeforces Gym101667 A. Broadcast Stations
題意:
給定一棵樹,可以選定一些點在上面放基站。
若節點\(u\)上的基站(如果有的話)的輻射範圍為\(d\),那麼距離\(u\)小於等於\(d\)的點會被覆蓋。放一個輻射範圍為\(d\)的基站需要花費\(d\)。
問:使得整棵樹被覆蓋的最小花費。
分析:
嘗試進行動態規劃。
我們很自然地考慮以\(u\)為根的樹被完全覆蓋的情況,設\(u\)有子節點\(v_1,v_2,\dots,v_m\)。如果令\(f(u)\)為以\(u\)為根節點的樹被覆蓋的最小花費,就需要考慮\(f(u)\)可以怎樣由子節點推出。這本質上就是考慮\(u\)
為了細化狀態,我們可以考慮這樣定義,令\(f(u,d)\)為以\(u\)為根的樹被完全覆蓋且對於\(u\)還至少能夠繼續延伸距離\(d\)的範圍的最小花費,換句話說\(f(u,d)\)是以\(u\)為根的樹被完全覆蓋且距離\(u\)小於等於\(d\)的點被全部覆蓋的最小花費,這樣可以看出\(f(u,d)\)和\(f(v,d+1)\)相關。然而我們發現就算是這樣還是不夠,為什麼呢?因為對於以\(u\)
為了解決這個問題,我們需要定義另一個狀態,令\(g(u,d)\)為在以\(u\)為根節點的樹的內部,距離\(u\)大於等於\(d\)的節點被全部覆蓋的最小花費。這樣就可以寫出具體的狀態轉移了。
\[g(u,d)=\min\{g(u,d-1),\sum\limits_{i=1}^mg(v_i,d-1)\},\qquad d\geq1\\ f(u,d)=\min\left\{f(u,d+1),d+\sum\limits_{i=1}^mg(v_i,d),\min\limits_{1\leq i\leq m}\left\{f(v_i,d+1)+\sum\limits_{1\leq j\leq m\and i\neq j}g(v_j,d)\right\}\right\},\qquad d\geq1\\ f(u,0)=\min\left\{f(u,1),\min\limits_{1\leq i\leq m}\left\{f(v_i,1)+\sum\limits_{1\leq j\leq m\and i\neq j}g(v_j,0)\right\}\right\}\\ g(u,0)=f(u,0) \]尤其注意第\(3\)
第\(3\)個式子不存在父親上放基站延伸到以子節點為根的樹的情況。
第\(4\)個式子不能遺漏。
注意到\(\sum\limits_{i=1}^mg(v_i,d)=g(u,d+1)\),以及\(\sum\limits_{1\leq j\leq m\and i\neq j}g(v_j,d)=g(u,d+1)-g(v_i,d)\)
且對於葉節點,\(f(u,d)=d(d\geq1)\),\(f(u,0)=1\),\(g(u,d)=0(d\geq1)\),\(g(u,0)=1\)
所以轉移部分,轉化成程式碼可以寫成
for (auto v : G[u]) {
if (v == fa) continue;
for (int d = 1; d < n; d++) {
// 先不用急著取最小值(事實上也不能,因為g[u][0]還不知道)
// 只計算和,運算結果對計算f[u][d]有幫助
g[u][d] += g[v][d - 1];
}
}
f[u][n - 1] = n - 1;
for (int d = n - 2; d >= 0; d--) {
if (d > 0) f[u][d] = min(f[u][d + 1], d + g[u][d + 1]);
else f[u][0] = f[u][1];
for (auto v : G[u]) {
if (v == fa) continue;
f[u][d] = min(f[u][d], f[v][d + 1] + g[u][d + 1] - g[v][d]);
}
}
g[u][0] = f[u][0];
for (int d = 1; d < n; d++) g[u][d] = min(g[u][d - 1], g[u][d]);
程式碼:
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 5000 + 10;
vector<int> G[maxn];
int f[maxn][maxn], g[maxn][maxn];
int n;
void dfs(int u, int fa) {
for (auto v : G[u]) {
if (v == fa)
continue;
dfs(v, u);
for (int d = 1; d < n; d++)
g[u][d] += g[v][d - 1];
}
f[u][n - 1] = n - 1;
for (int d = n - 2; d >= 0; d--) {
if (d > 0)
f[u][d] = min(f[u][d + 1], d + g[u][d + 1]);
else
f[u][0] = f[u][1];
for (auto v : G[u]) {
if (v == fa)
continue;
f[u][d] = min(f[u][d], f[v][d + 1] + g[u][d + 1] - g[v][d]);
}
}
g[u][0] = f[u][0];
for (int d = 1; d < n; d++)
g[u][d] = min(g[u][d - 1], g[u][d]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
printf("%d\n", g[1][0]);
return 0;
}