DP中的树上边/点覆盖问题
- 树上边覆盖问题
- 例:luoguP2016 战略游戏
- 简述题意:
- Solution:
- Code
- 树上点覆盖问题
- 简述题意
- Solution
- Code:
树上边覆盖问题
例:luoguP2016 战略游戏
简述题意:
每个节点都能放一个士兵,每个士兵能看守与他相邻的所有边,求覆盖所有边最少需要多少士兵?
Solution:
设 \(f[x][1/0]\) 表示回溯到第 \(x\) 个点时所用士兵数量, \(1\) 表示在这里放一个士兵, \(0\) 表示不放
显然珂推得转移方程:
\[f[x][1] = 1 + \sum_{v \in son[x]}min(f[v][1],f[v][0]) \]\[f[x][0] = \sum_{v \in son[x]} f[v][1] \]Code
#include
#include
using namespace std;
const int MAXN = 1610;
struct edge{
int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge;
int n, k;
int f[MAXN][2];
int read(){
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
return s * w;
}
void add_edge(int from, int to){ e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
void dfs(int x, int fa){
f[x][1] = 1;
for(int i = head[x]; i; i = e[i].nxt){
int v = e[i].to;
if(v == fa) continue;
dfs(v, x);
f[x][1] += min(f[v][1], f[v][0]);
f[x][0] += f[v][1];
}
}
int main()
{
n = read();
for(int i = 1, x; i <= n; ++i){
x = read(), x++;
k = read();
for(int j = 1, v; j <= k; ++j){
v = read(), v++;
add_edge(x, v), add_edge(v, x);
}
}
dfs(1, 0);
printf("%d", min(f[1][1], f[1][0]));
return 0;
}
树上点覆盖问题
例:P2458 [SDOI2006]保安站岗
简述题意
每个保安可以保护自己的点和相邻点,求将树上所有点都覆盖最少所需保安数
Solution
设 \(f[u][0/1/2]\) 表示回溯到第 \(u\) 个点时所用士兵数量, \(0\) 表示在自己这里放一个士兵, \(1\) 表示被儿子覆盖, \(2\) 表示被父亲覆盖,
(其中 \(x\) 点是用来覆盖它父亲 \(u\) 的点)
转移方程:
\[f[u][0] = \sum_{v \in son[u]} min(f[v][0],f[v][1],f[v][2]) \]\[f[u][1] = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1]) \]\[f[u][2] = \sum_{v \in son[u]} min(f[v][0], f[v][1]) \]感觉 \(f[u][0]\) 和 \(f[u][2]\) 的转移方程都比较显然
对于 \(f[u][1]\) 因为不同于树上边覆盖问题,它可以被自己的儿子覆盖,所以对儿子的要求是:要么是被自己覆盖,要么被自己的儿子覆盖
但对于 \(u\) 本身要保证自己的儿子中有一个是被自己覆盖,所以要求出最优的那个儿子 \(x\) 就好了,可以枚举所有儿子,这里介绍一种数学式子优化
最优的 \(x\) 满足 \(f[x][0] - min(f[x][0], f[x][1])\) 最小
参考题解
证明:
因为 \(x\) 满足 \(f[u][1] = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1])\)
设 \(F(u, x) = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1])\)
假设 \(x\) 不是最优的, 则必有一个 \(y\) 满足 \(F(u, x) > F(u, y)\)
将这个式子化简得(可以将相同的部分消掉)
\(f[x][0] - min(f[x][0], f[x][1]) > f[y][0] - min(f[y][0], f[y][1])\)
所以有最优的 \(x\) 满足 \(f[x][0] - min(f[x][0], f[x][1])\) 最小
证毕
下面是代码时间:
Code:
/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include
#include
#include
#include
#include
#define LL long long
#define orz cout<<"lkp AK IOI!"< f[v][0] - min(f[v][0], f[v][1])) x = v;
}
f[u][1] = f[x][0];
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa || v == x) continue;
f[u][1] += min(f[v][0], f[v][1]);
}
}
int main()
{
n = read();
for(int i = 1, u; i <= n; ++i) {
u = read(), f[u][0] = read(), m = read();
for(int j = 1, v; j <= m; ++j) {
v = read();
add_edge(u, v), add_edge(v, u);
}
}
f[0][0] = INF;
dfs(1, 0);
printf("%d", min(f[1][0], f[1][1]));
return 0;
}
其他两个树上点覆盖问题例题,稍微改一下输入即可,一个套路:
P2899 [USACO08JAN]Cell Phone Network G
T155737 搬书
最后欢迎大家来补充啊,团队私题要是涉及隐私的话可以联系我移除