樹形dp(以點爲單位)

這一題與《戰略遊戲》那道題還是有些不同的

上一題狀態設計爲每個節點選或不選,在轉移的時候每條邊至少選一個就可以維護出當前子樹中正確的選法中最小的

本題就需要更爲複雜的轉移了,因爲你不能通過當前父節點選或不選來判斷出子節點選或不選,本題要求每個節點的  父節點,子節點,本身  中的一個節點被選,因此狀態表示爲 f[x][j]

f[x][0]  第x個節點的父節點被選(當前點未選)

f[x][1]  第x個節點有一個子節點被選(當前點未選)

f[x][2]  第x個節點本身被選

f[x][0]+=min(f[y][1],f[y][2])

f[x][2]+=min{f[y][0],f[y][1],f[y][2]}

f[x][1]+=min{f[k][2]+min(f[y][1],f[y][2])} (k節點是x的子節點中最合適的,其他子節點取爲min(f[y][1],f[y][2]) )

 

皇宮看守

太平王世子事件後,陸小鳳成了皇上特聘的御前一品侍衛。

皇宮以午門爲起點,直到後宮嬪妃們的寢宮,呈一棵樹的形狀,某些宮殿間可以互相望見。

大內保衛森嚴,三步一崗,五步一哨,每個宮殿都要有人全天候看守,在不同的宮殿安排看守所需的費用不同。

可是陸小鳳手上的經費不足,無論如何也沒法在每個宮殿都安置留守侍衛。

幫助陸小鳳佈置侍衛,在看守全部宮殿的前提下,使得花費的經費最少。

輸入格式

輸入中數據描述一棵樹,描述如下:

第一行 n,表示樹中結點的數目。

第二行至第 n+1 行,每行描述每個宮殿結點信息,依次爲:該宮殿結點標號 i,在該宮殿安置侍衛所需的經費 k,該結點的子結點數 m,接下來 m 個數,分別是這個結點的 m 個子結點的標號 r1,r2,…,rm。

對於一個 n 個結點的樹,結點標號在 1 到 n 之間,且標號不重複。

輸出格式

輸出一個整數,表示最少的經費。

數據範圍

1≤n≤1500

輸入樣例:

6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0

輸出樣例:

25

樣例解釋:

在2、3、4結點安排護衛,可以觀察到全部宮殿,所需經費最少,爲 16 + 5 + 4 = 25。

#include <iostream>
#include <vector>
using namespace std;
const int N = 1500+10,INF=0x3f3f3f3f;
vector<int>son[N];
int cost[N],f[N][3],have[N];
void dp(int x){
    f[x][2]=cost[x];
    int res=INF;
    for(int i=0;i<son[x].size();i++){
        int y=son[x][i];
        dp(y);
        f[x][0]+=min(f[y][1],f[y][2]);
        f[x][1]+=min(f[y][1],f[y][2]);
        f[x][2]+=min(f[y][0],min(f[y][1],f[y][2]));
        res=min(res,f[y][2]-min(f[y][1],f[y][2]));
    }
    f[x][1]+=res;
    if(f[x][1]==0) f[x][1]=INF;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x,m,y;
        scanf("%d",&x);
        scanf("%d%d",&cost[x],&m);
        while(m--){
            scanf("%d",&y);
            son[x].push_back(y);
            have[y]=1;
        }
    }
    int root=1;
    while(have[root]==1) root++;
    dp(root);
    printf("%d",min(f[root][1],f[root][2]));
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章