【题解】UVa 12093 Protecting Zonk

UVa传送门
洛谷RemoteJudge传送门
题目大意:给定一个 n ( n 10000 )个结点的无根树。有两种装置 A B ,每种都有无限多个。
在某个结点 X 安装 A 需要 C 1 的花费,并且此时与结点 X 相连的边都被覆盖。
在某个结点 X 安装 B 需要 C 2 的花费,并且此时与结点 X 相连的边和与 X 相连的点的边都会被覆盖。
求覆盖所有边的最小花费。

我们先从 1 号点跑一遍dfs,将无根树转化成有根树。
观察到结点 u 一共可能会有四种状态,我们对其加以分类:
d ( u , 0 ) 表示 u 没有安装装置,且 u 的子节点与 u 相连的边都需要被覆盖。
d ( u , 1 ) 表示 u 安装 A 装置
d ( u , 2 ) 表示 u 安装 B 装置
d ( u , 3 ) 表示 u 没有安装装置,且 u 的子节点不必安装装置。

状态转移方程

d ( u , 0 ) = m i n ( d ( v , 1 ) , d ( v , 2 ) )
d ( u , 1 ) = m i n ( d ( v , 0 ) , d ( v , 1 ) , d ( v , 2 ) ) + C 1
d ( u , 2 ) = m i n ( d ( v , 0 ) , d ( v , 1 ) , d ( v , 2 ) , d ( v , 3 ) ) + C 2
d ( u , 3 ) = m i n ( d ( v , 0 ) , d ( v , 1 ) , d ( v , 2 ) )
这样的状态转移方程是不完整的,同时也是一个大坑,原因在于只要 u 有一个子节点安装了 B 装置, u 的所有子节点都会被覆盖
所以, d ( u , 1 ) 还要加一个特殊处理,当至少有一个子节点选 B 时对 m i n ( d ( v , 0 ) , d ( v , 1 ) , d ( v , 2 ) ) 取个 m i n
这样,状态转移方程就完整了。

C o d e

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

猜你喜欢

转载自blog.csdn.net/diogenes_/article/details/80556870