【題解】UVa 12093 Protecting Zonk

UVa傳送門
洛谷RemoteJudge傳送門
題目大意:給定一個n (n10000 )個結點的無根樹。有兩種裝置AB ,每種都有無限多個。
在某個結點X 安裝A 需要C1 的花費,並且此時與結點X 相連的邊都被覆蓋。
在某個結點X 安裝B 需要C2 的花費,並且此時與結點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)=min(d(v,1),d(v,2))
d(u,1)=min(d(v,0),d(v,1),d(v,2))+C1
d(u,2)=min(d(v,0),d(v,1),d(v,2),d(v,3))+C2
d(u,3)=min(d(v,0),d(v,1),d(v,2))
這樣的狀態轉移方程是不完整的,同時也是一個大坑,原因在於只要u 有一個子節點安裝了B 裝置,u 的所有子節點都會被覆蓋
所以,d(u,1) 還要加一個特殊處理,當至少有一個子節點選B 時對min(d(v,0),d(v,1),d(v,2)) 取個min
這樣,狀態轉移方程就完整了。

Code

#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;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章