P2016 战略游戏
知识点: 树形DP
原题面
题目要求:
给定一棵无根树,
花费 \(1\) 价值, 覆盖一个点 , 可以使该点为一端的所有边 被覆盖
求 将所有边全部覆盖的 最小价值
分析题意 :
-
对题目要求进行转化 :
-
由于一条边只有 两端点 ,
若要覆盖将所有边 , 则每一条边至少有一个端点 被覆盖 -
则题目要求转化为:
给定一棵无根树,
花费 \(1\) 价值, 可覆盖一个点
求使所有边 至少有一个端点 被覆盖的价值
-
-
对于一个 节点 :
将其覆盖后, 只会影响其直接相邻的边,
不直接相邻的边 不会受影响
显然 , 可以进行树形 \(DP\) :-
设计状态 :
先随意选择一个点 作为根节点
设 \(\text{f[u][0/1]}\) 表示 : 以 \(u\) 节点为根的子树中, \(u\) 节点 不覆盖\(/\)覆盖 时, 满足条件的 最小价值和 -
初始化: \(\text{f[u][1] = 1, f[u][0] = 0}\);
-
状态转移 :
- 若父节点不被覆盖, 则其直接子节点必然被覆盖, 否则不合法
则有: \(f[u][0] += \sum(f[v][1])\); - 父节点被覆盖, 则其直接子节点 可以覆盖/不覆盖, 选择较小的计入贡献
则有: \(f[u][1] += \sum min(f[v][0], f[v][1]))\)
- 若父节点不被覆盖, 则其直接子节点必然被覆盖, 否则不合法
最后输出 \(min(f[root][0], f[root][1])\) 即可
-
#include <cstdio>
#include <ctype.h>
#include <vector>
#include <algorithm>
#define min std :: min
const int MARX = 1e4 + 10;
//=============================================================
struct Edge
{
int u, v, ne;
}e[MARX << 1];
int N, num, head[MARX];
int f[MARX][2];//1自己 , 0儿子
//=============================================================
inline int read()
{
int s = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
return s * w;
}
void addedge(int u, int v)
{
e[++ num].u = u, e[num].v = v;
e[num].ne = head[u], head[u] = num;
}
void dfs(int u, int fa)//dfs 进行树形DP
{
f[u][1] = 1;//初始化
for(int i = head[u]; i; i = e[i].ne)
if(e[i].v != fa)
{
dfs(e[i].v, u);//优先更新 子节点
//状态转移:
f[u][0] += f[e[i].v][1];//父节点 不覆盖
f[u][1] += min(f[e[i].v][0], f[e[i].v][1]);//父节点 覆盖
}
}
//=============================================================
signed main()
{
N = read();
for(int i = 1; i <= N; i ++)//建图
{
int u = read(), m = read();
for(int j = 1; j <= m; j ++)
{
int v = read();
addedge(u, v), addedge(v, u);
}
}
dfs(0, 0);
printf("%d", min(f[0][0], f[0][1]));//取最小值 作为答案
}
作者@Luckyblock,转载请声明出处。