• 洛谷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} = sumlimits_{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} = sumlimits_{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;
    }
    
  • 相关阅读:
    oracle 创建数据库 创建表空间 创建用户
    Oracle 10G/11G 导入 导出
    winform与IE交互
    asihttprequest简单异步
    架构师的故事
    duobangotinySDP,rfc 2327
    duobangotinySAK,20121213
    doubango框架阅读计划
    并读<自己动手做操作系统>及<汇编语言2>
    SBJSON在xcode的应用中需要注意的
  • 原文地址:https://www.cnblogs.com/quanjun/p/15369186.html
Copyright © 2020-2023  润新知