P2458 [SDOI2006]保安站岗 树形dp最小费用覆盖点

P2458 [SDOI2006]保安站岗 树形dp最小费用覆盖点


传送门: https://www.luogu.com.cn/problem/P2458

题意

给 一 棵 树 , 在 树 上 安 置 保 安 , 一 条 边 的 两 个 端 点 至 少 安 置 一 个 保 安 , 保 安 都 有 费 用 。 给一棵树,在树上安置保安,一条边的两个端点至少安置一个保安,保安都有费用。
问 符 合 情 况 的 最 小 费 用 是 多 少 。 问符合情况的最小费用是多少。

思路

因 为 一 条 边 的 两 个 端 点 必 须 要 有 一 个 , 可 以 有 两 个 , 所 以 这 是 不 限 制 父 亲 和 儿 子 关 系 的 。 因为一条边的两个端点必须要有一个,可以有两个,所以这是不限制父亲和儿子关系的。

如 果 一 条 边 的 两 个 端 点 都 选 , 那 一 定 不 会 是 最 小 费 用 , 所 以 只 需 要 选 一 点 即 可 。 如果一条边的两个端点都选,那一定不会是最小费用,所以只需要选一点即可。

对 于 一 个 点 选 不 选 , 对 父 亲 和 儿 子 选 不 选 都 有 影 响 , 所 以 设 f [ u ] [ 0 / 1 / 2 ] 。 对于一个点选不选,对父亲和儿子选不选都有影响,所以设f[u][0/1/2]。 f[u][0/1/2]
f [ u ] [ ] 表 示 以 u 为 根 的 子 树 。 f[u][]表示以u为根的子树。 f[u][]u

  • f [ u ] [ 0 ] 表 示 u 自 己 选 f[u][0]表示u自己选 f[u][0]u
  • f [ u ] [ 1 ] 表 示 自 己 不 选 , 儿 子 选 f[u][1]表示自己不选,儿子选 f[u][1]
  • f [ u ] [ 2 ] 表 示 自 己 不 选 , 父 亲 选 f[u][2]表示自己不选,父亲选 f[u][2]

转 移 方 程 : 转移方程:

自 己 选 , 不 妨 碍 其 他 点 的 选 择 , 即 自己选,不妨碍其他点的选择,即

  • f [ u ] [ 0 ] + = ∑ v ∈ g [ u ] m i n ( f [ v ] [ 0 ] , f [ v ] [ 1 ] , f [ v ] [ 2 ] ) f[u][0]+=\sum_{v\in g[u]}min(f[v][0],f[v][1],f[v][2]) f[u][0]+=vg[u]min(f[v][0],f[v][1],f[v][2])

父 亲 选 , 所 以 自 己 不 用 选 了 , 只 需 要 加 上 儿 子 的 最 小 费 用 , 即 父亲选,所以自己不用选了,只需要加上儿子的最小费用,即

  • f [ u ] [ 2 ] + = ∑ v ∈ g [ u ] m i n ( f [ v ] [ 0 ] + f [ v ] [ 1 ] ) f[u][2]+=\sum_{v\in g[u]} min(f[v][0]+f[v][1]) f[u][2]+=vg[u]min(f[v][0]+f[v][1])

儿 子 选 , 对 于 所 有 儿 子 , 肯 定 不 会 全 部 都 选 , 选 择 最 小 的 儿 子 即 可 , 即 儿子选,对于所有儿子,肯定不会全部都选,选择最小的儿子即可,即

  • f [ u ] [ 1 ] + = ∑ v ∈ g [ u ] 除 最 小 f [ v ] [ 1 ] + f [ v 最 优 ] [ 0 ] f[u][1]+=\sum_{v\in g[u]除最小}f[v][1]+f[v_{最优}][0] f[u][1]+=vg[u]f[v][1]+f[v][0]

对 于 f [ u ] [ 1 ] , 我 们 怎 么 找 到 最 小 的 呢 ? 对于f[u][1],我们怎么找到最小的呢? f[u][1]

记 录 m n = m i n ( m n , f [ v ] [ 0 ] − f [ v ] [ 1 ] ) 记录mn=min(mn,f[v][0]-f[v][1]) mn=min(mn,f[v][0]f[v][1])

	bool flag = 1;
	if(f[v][0] <= f[v][1]) {
            flag = 0;
            f[u][1] += f[v][0];
        }
        else {
            f[u][1] += f[v][1];
            mn = min(mn, f[v][0] - f[v][1]);
        }
    }
    if(flag) f[u][1] += mn;

Code

#include "bits/stdc++.h"
using namespace std;

const int N = 1e4 + 10;
vector<int> g[N];
int val[N];
int f[N][3];

void dfs(int u, int fa) {
    f[u][0] = val[u];
    bool flag = 1;
    int mn = 1e9;
    for(auto v : g[u]) {
        if(v == fa) continue;
        dfs(v, u);
        f[u][0] += min(f[v][0], min(f[v][1], f[v][2]));
        f[u][2] += min(f[v][0], f[v][1]);
        if(f[v][0] <= f[v][1]) {
            flag = 0;
            f[u][1] += f[v][0];
        }
        else {
            f[u][1] += f[v][1];
            mn = min(mn, f[v][0] - f[v][1]);
        }
    }
    if(flag) f[u][1] += mn;
}

void solve() {
    int n; cin >> n;
    for(int i = 1;i <= n; i++) {
        int id, m; cin >> id >> val[id] >> m;
        for(int j = 1;j <= m; j++) {
            int v; cin >> v;
            g[v].eb(id);
            g[id].eb(v);
        }
    }
    dfs(1, -1);
    cout << min(f[1][0], f[1][1]) << endl;
}

signed main() {
    solve();
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值