Files
python/TangDou/AcWing/Tree/1077.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

82 lines
2.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1510;
int n;
int h[N], e[N], ne[N], idx;
int w[N]; // 点的权值,不是边,不是边,不是边!
/*
状态表示:
(1)集合
f[u][0]:u号节点不放守卫,被父节点看守的方案
f[u][1]:u号节点不放守卫,被某个子节点看守的方案
f[u][2]:u号节点自己安排守卫看住的方案
(2)属性
花费最小值
*/
int f[N][3];
int in[N]; // 入度
// 邻接表
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
// 初始化
f[u][0] = 0; // 被父亲守护,对于u子树而言,最小值为0
f[u][1] = INF; // 被某个子节点守护,但还没有评选中由哪个来看守更合适,预求最小先设最大
f[u][2] = w[u]; // 被自己守护,对于u子树而言,代价为w[u]
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
dfs(v);
// ① u不放守卫,被父节点守护,它的儿子们可以放守卫,也可以不放守卫,两者代价取min
f[u][0] += min(f[v][1], f[v][2]);
// ③ u放守卫,儿子可以指望父亲,可以自己放守卫,也可以指望某个孙子来守卫,三者代价取min
f[u][2] += min({f[v][0], f[v][1], f[v][2]});
}
// ② u不放守卫,由某个儿子v守护
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
// Q:f[u][0]是什么意思?
// 向上看第39行,本质上就是所有子节点v1,v2,v3,...的最小花费值累加和
// 假设v是孝顺儿子,需要在其中扣除掉v的min(f[v][1],f[v][2])贡献
// 它的代价跑不了,此时就不能取min了,而是实打实的f[v][2]
f[u][1] = min(f[u][1], f[v][2] + f[u][0] - min(f[v][1], f[v][2]));
}
}
int main() {
memset(h, -1, sizeof h); // 初始化
cin >> n; // 节点数
for (int i = 1; i <= n; i++) { // n个节点
int x, m; // 节点号,代价,子节点数
cin >> x >> w[x] >> m;
while (m--) {
int y;
cin >> y;
add(x, y); // 有向图
in[y]++; // 入度,用于找根
}
}
// 找根
int root = 1;
while (in[root]) root++;
// dfs
dfs(root);
// f[root][0]表示root不放守卫,被父节点看守,根节点是没有父节点的, 所以此状态是无效状态,不参与结果讨论
// 最终最小代价值取min(f[root][1],f[root][2])
printf("%d\n", min(f[root][1], f[root][2]));
return 0;
}