洛谷P2899 Cell Phone Network G 题解

题目链接:https://www.luogu.com.cn/problem/P2899

题目大意:给你一棵树,在树中选择一些节点,使得树中的每个节点要么是选择的节点,要么和至少一个选择的节点相邻。求:最少选择节点个数。

解题思路:树形DP。对于每个节点 \(u\),定义状态:

  • \(f_{u,0}\):以 \(u\) 为根的子树中,\(u\) 没有选但是 \(u\) 的父节点选了(假设 \(u\) 有父节点)的情况下的最小选择节点数;
  • \(f_{u,1}\):以 \(u\) 为根的子树中,\(u\) 选择了的情况下的最小选择节点数;
  • \(f_{u,2}\):以 \(u\) 为根的子树中,\(u\) 没有选,但是 \(u\) 的所有子节点中至少有一个选择了的情况下的最小选择节点数。

则,状态转移方程为(用 \(v\) 来表示左右 \(u\) 的子节点集合中的元素):

\(f_{u,0}\)

\(f_{u,0} = \sum\limits_{v} \min\{ f_{v,1}, f_{v,2} \}\)

  • 因为此时 \(u\) 没有选,但是它的(即将到来的)父节点会选,所以对于 \(u\) 的子节点 \(v\) 来说,可以选(\(f_{v,1}\))也可以不选,但是不选的情况下因为 \(u\)\(v\) 都没有选了,所以必须得保证 \(v\) 至少有一个子节点是选择了的(对应状态 \(f_{v,2}\)

\(f_{u,1}\)

\(f_{u,1} = \sum\limits_{v} \min\{ f_{v,0}, f_{v,1} \}\)

  • \(u\) 选了的情况下,它的子节点 \(v\) 可以选(对应状态 \(f_{v,1}\))也可以不选,但是不选的时候对于 \(v\) 来说它的父节点是选择了的(刚好对应 \(f_{v,0}\)

\(f_{u,2}\)

\(f_{u,2}\) 的计算比较特殊一点。

  • \(u\) 的子节点 \(v\) 中存在至少一个节点 \(v\) 满足 \(f_{v,1} \le f_{v,0}\),则说明可以在选择 \(v\) 的情况下达到 \(f_{u,0}\) 的最小是,则此时 \(f_{u,2} = f_{u,0}\)
  • 否则(所有 \(v\) 都是 \(f_{v,1} \gt f_{v,0}\)),则需要选择一个 \(f_{v,1} - f_{v,0}\) 最小的一个 \(v\)(设为 \(v'\)),\(f_{u,2} = f_{u,0} - f_{v',0} + f_{v', 1}\)

当然,其中还存在一些不合法的情况,比如叶子节点的 \(f_{u,2}\) 就不可能存在,将其设为 \(+ \infty\) 即可(我代码中设为了 \(n\),因为所有的状态都不会超过 \(n\))。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int n, f[maxn][3];
vector<int> g[maxn];
void dfs(int u, int p) {
    f[u][0] = f[u][2] = 0;
    f[u][1] = 1;
    bool flag = false;
    for (auto v : g[u]) {
        if (v != p) {
            dfs(v, u);
            f[u][0] += min(f[v][1], f[v][2]);
            f[u][1] += min(f[v][0], f[v][1]);
            if (f[v][1] <= f[v][2]) flag = true;
        }
    }
    if (flag) f[u][2] = f[u][0];
    else {
        f[u][2] = n;
        for (auto v : g[u]) {
            if (v != p) {
                f[u][2] = min(f[u][2], f[u][0] - f[v][2] + f[v][1]);
            }
        }
        if (f[u][2] == 0) f[u][2] = n;
    }
}
int main() {
    cin >> n;
    for (int i = 1; i < n; i ++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, -1);
    cout << min(f[1][2], f[1][1]) << endl;
    return 0;
}
posted @ 2021-10-05 18:29  quanjun  阅读(103)  评论(0编辑  收藏  举报