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

 

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