总结:树上最小点覆盖
总结:树上最小点覆盖
本篇总结了:
- 标准树上最小点覆盖
- 标准树上最小关键点覆盖
- 标准树上带权最小点覆盖
- \(k\) 度树上最小点覆盖
- \(k\) 度树上最小关键点覆盖
这里 \(k\) 度最小点覆盖含义:
选择一个点后,与其最短距离不超过 \(k\) 的节点都被算作覆盖。
标准树上最小点覆盖
经典树 DP,考虑对于每个子树进行 3 种分讨:
- 选儿子覆盖自己
- 选自己覆盖自己
- 选父亲覆盖自己
转移即可。
标准树上最小关键点覆盖
对比上面的做法,在非关键点时直接换一种转移即可
标准树上带权最小点覆盖
这里“带权”显然是带点权
设点 \(i\) 的点权为 \(a_i\)
对比上面的做法,将转移方程中的 +1 换成 +a[i] 即可。
\(k\) 度树上最小点覆盖
例题:
P3942 将军令
这时再向上面那样 DP 的话情况太多了,考虑贪心
发现在一个有根树上,如果选 \(u\) 这个点,满足 \(u\) 子树内始终都被覆盖的情况下,选 \(u\) 的父亲,答案一定不会比 \(u\) 劣
那么我们就有一个很明显的贪心,记 \(h_i\) 为 \(i\) 号点到最远未被覆盖点的距离,只有当这个距离为 \(k\) 时才选。
但是这样不一定最优,考虑最远未被覆盖的点可能被子树内一个选过的点以一个长度不大于 \(k\) 的折线路径覆盖,因此我们还要记 \(f_i\) 为 \(i\) 号点到子树内被覆盖点的最短距离。当 \(f_i+h_i\leq k\) 时,即使满足 \(h_i=k\), \(i\) 号点也不用选。
核心代码:
void dfs(int u, int fa)
{
f[u] = INF;
h[u] = 0;
for (int i = 0; i < g[u].size(); i ++)
{
int v = g[u][i];
if (v == fa)
continue;
dfs(v, u);
f[u] = min(f[u], f[v] + 1);
h[u] = max(h[u], h[v] + 1);
}
if (h[u] == k)
{
m ++;
f[u] = 0;
h[u] =-1;
}
if (f[u] + h[u] <= k)
h[u] =-1;
}
\(k\) 度树上最小关键点覆盖
考虑二分一下答案就变成了 \(k\) 度树上最小关键点覆盖问题
对比上面的,这里仅需加上一句即可
if (f[u] > k && key[u] == 1)
h[u] = max(h[u], 0);
即如果当前关键点未被其子树内的点覆盖,就让上面的点将其覆盖。
注意:
在求 \(h_i\) 时不要误将已经被覆盖的点当成未被覆盖的关键点转移

浙公网安备 33010602011771号