UVa传送门
洛谷RemoteJudge传送门
题目大意:给定一个
(
)个结点的无根树。有两种装置
和
,每种都有无限多个。
在某个结点
安装
需要
的花费,并且此时与结点
相连的边都被覆盖。
在某个结点
安装
需要
的花费,并且此时与结点
相连的边和与
相连的点的边都会被覆盖。
求覆盖所有边的最小花费。
我们先从
号点跑一遍dfs,将无根树转化成有根树。
观察到结点
一共可能会有四种状态,我们对其加以分类:
表示
没有安装装置,且
的子节点与
相连的边都需要被覆盖。
表示
安装
装置
表示
安装
装置
表示
没有安装装置,且
的子节点不必安装装置。
状态转移方程
这样的状态转移方程是不完整的,同时也是一个大坑,原因在于只要
有一个子节点安装了
装置,
的所有子节点都会被覆盖
所以,
还要加一个特殊处理,当至少有一个子节点选
时对
取个
。
这样,状态转移方程就完整了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define MAXN 100010
#define INF 0x3f3f3f3f
struct EdgeType {
int to, next;
};
std::vector< EdgeType > edge;
int head[MAXN], d[MAXN][4];
inline int _min(int a, int b, int c) {
return std::min(a, std::min(b, c));
}
inline void AddEdge(int u, int v) {
edge.push_back((EdgeType){v, head[u]});
head[u] = edge.size() - 1;
}
void solve(int u, int p) {
int sum = 0, min = INF;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (v == p) continue;
solve(v, u);
d[u][0] += std::min(d[v][1], d[v][2]);
d[u][1] += _min(d[v][0], d[v][1], d[v][2]);
d[u][2] += std::min(_min(d[v][0], d[v][1], d[v][2]), d[v][3]);
d[u][3] += _min(d[v][0], d[v][1], d[v][2]);
int temp = _min(d[v][0], d[v][1], d[v][2]);
sum += temp;
min = std::min(min, d[v][2] - temp);
}
d[u][1] = std::min(d[u][1], sum + min);
}
int main() {
int n, p, q;
while (scanf("%d%d%d", &n, &p, &q) != EOF && n != 0) {
memset(head, -1, sizeof head);
edge.clear();
for (int i = 1; i <= n; i++) {
d[i][0] = 0;
d[i][1] = p;
d[i][2] = q;
d[i][3] = 0;
}
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
AddEdge(u, v);
AddEdge(v, u);
}
solve(1, -1);
printf("%d\n", _min(d[1][0], d[1][1], d[1][2]));
}
return 0;
}