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\)处放不放基站,以及放辐射范围为多少的基站。我们发现,要覆盖\(u\)有两种类型,一种是\(u\)本身放基站,另一种是以子节点为根节点的树的覆盖范围延伸到\(u\)节点。这个时候我们会发现\(f(u)\)这个状态定义太“粗”了,需要细化。

为了细化状态,我们可以考虑这样定义,令\(f(u,d)\)为以\(u\)为根的树被完全覆盖且对于\(u\)还至少能够继续延伸距离\(d\)的范围的最小花费,换句话说\(f(u,d)\)是以\(u\)为根的树被完全覆盖且距离\(u\)小于等于\(d\)的点被全部覆盖的最小花费,这样可以看出\(f(u,d)\)\(f(v,d+1)\)相关。然而我们发现就算是这样还是不够,为什么呢?因为对于以\(u\)的子节点为根的树而言是可以通过父节点\(u\)相互延伸的,而且\(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\)个和第\(4\)个式子(边界的情况)

\(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;
}

Codeforces Gym101667 A. Broadcast Stations 树形DP

上一篇:chapter2 ROS2 概念理解


下一篇:C#开发和调用Web Service