1. 程式人生 > >Vijos1144 皇宮看守 (0/1/2三種狀態的普通樹形Dp)

Vijos1144 皇宮看守 (0/1/2三種狀態的普通樹形Dp)

題意:

給出一個樹以及一些覆蓋每個點的花費,求每個點都能被自己被覆蓋,或者相鄰的點被覆蓋的最小价值。

細節:

其實我乍一眼看過去還以為是 戰略遊戲 的複製版 可愛的戰略遊戲在這裡QAQ(請原諒這波廣告)

顯然這是一個巨坑,所以必須先來看一張神奇的圖片~ ~ ~ ~ ~
帥氣的圖片在這裡QVQ

我們顯然很輕鬆的找出了反例,那該如何解決呢?

分析:

不難發現對於從上往下的第二個節點,它的兒子和它都不需要被覆蓋也是合法的,所以對於一個節點在圖中來說,合法狀態有:它被覆蓋其兒子父親都被覆蓋或都不被或其中一個被覆蓋,它不被覆蓋其兒子父親都被覆蓋或其中一個被覆蓋。

所以對於一個節點來說它的狀態有:1.它的父親被覆蓋; 2.它的兒子被覆蓋; 3.它自己被覆蓋;
所以狀態 dp[u][0/1/2] 表示的就是上述三種情況
轉移如下:

dp[u][0] = ∑ min( dp[v][1] , dp[v][2])
dp[u][1] = ∑ min( dp[v][1] , dp[v][2]) + del
dp[u][2] = ∑ min( dp[v][1] , dp[v][2] , dp[v][0] ) + val[u]
del = min( dp[v][2] - min(dp[v][1] , dp[v][2] )

del 指的是什麼?首先關鍵還是在於狀態節點 u

被其兒子覆蓋的合法狀態,這就說明了其兒子之中必須存在一個節點是把自己覆蓋的,所以我們只要最有加上這個 del 問題就迎刃而解啦~~~

程式碼:

#include<bits/stdc++.h>
#define MAXN 300005
#define LL long long
using namespace std;

int val[MAXN], n;
vector<int> Right[MAXN];
LL f[MAXN][3];

void dfs(int u, int fa){
    f[u][0]=0, f[u][1]=0, f[u][2]=val[u];
    LL del=3000000000ll;
    for (int i=0; i<Right[u].size(); i++) {
        int v=Right[u][i];
        if (v==fa) continue;
        dfs(v, u);
        f[u][0]+=min(f[v][1], f[v][2]);
        f[u][1]+=min(f[v][1], f[v][2]);
        f[u][2]+=min(f[v][0], min(f[v][1], f[v][2]));
        del=min(del, f[v][2]-min(f[v][1], f[v][2]));
    }
    f[u][1]+=del;
}

int main(){
    scanf("%d", &n);
    for (int i=1, x, num; i<=n; i++) {
        scanf("%d", &x);
        scanf("%d%d", &val[x], &num);
        for (int j=1, y; j<=num; j++){
            scanf("%d", &y);
            Right[x].push_back(y);
            Right[y].push_back(x);
        }
    }
    dfs(1, 0);
    printf("%lld\n", min(f[1][1], f[1][2]));
    return 0;
}