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;
}