其他分享
首页 > 其他分享> > 皇宫看守

皇宫看守

作者:互联网

原题链接

树形DP + 状态机

对于每个节点u有三种情况:
1.u点放置哨兵,u被自己观察到,那么u的子节点可放可不放,取min
2.u不放哨兵,但是u的任一子节点放置了哨兵,u被子节点观察到
3.u不放哨兵,u的父节点放置了哨兵,u被父节点观察到,那么u的子节点可放可不放

所以状态机定义三种模型
状态表示:
f[u][0]表示点u不放哨兵,u被父节点观察到
f[u][1]表示点u不放哨兵,u被子节点观察到
f[u][2]表示点u放哨兵,u被自己观察到

状态转移:
j 表示u的子节点
f[u][0] = ∑ min(f[j][1],f[j][2])
f[u][2] = ∑ min(f[j][0], f[j][1], f[j][2]) + w[u] // u放置哨兵,花费至少为为w[u]
对于f[u][1],找到一个子节点k观察到u,其余子节点j可放可不放
所以f[u][1] = min(f[k][2] + ∑ min(f[j][1], f[j][2]))

#include <iostream>
#include <algorithm>
#include <cstring>

const int N = 1505;

int w[N];
int h[N], e[N], ne[N], idx;
int f[N][3];
bool st[N];

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u) {
    f[u][2] = w[u];
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        dfs(j);
        f[u][0] += std::min(f[j][1], f[j][2]);
        f[u][2] += std::min({f[j][0], f[j][1], f[j][2]});
    }

    f[u][1] = 1e9;
    int& v = f[u][1];
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        // f[u][0]表示了sum{min(f[j][1], f[j][2])}
        // 用总和减去当前子节点的贡献就得到剩余可放可不放的子节点的花费
        v = std::min(v, f[u][0] + f[j][2] - std::min(f[j][1], f[j][2]));
    }
}

int main() {
    int n;
    std::cin >> n;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++ ) {
        int a, cost, m;
        std::cin >> a >> cost >> m;
        w[a] = cost;
        while (m -- ) {
            int b;
            std::cin >> b;
            add(a, b);
            st[b] = true;
        }
    }

    int root = 1;
    while (st[root]) root ++ ;
    dfs(root);
    std::cout << std::min(f[root][1], f[root][2]) << '\n';

    return 0;
}

标签:std,min,int,看守,ne,哨兵,皇宫,节点
来源: https://www.cnblogs.com/zjh-zjh/p/16701364.html