Vijos[1144]小胖守皇宫 树动规

题目链接:https://vijos.org/p/1144

描述

huyichen世子事件后,xuzhenyi成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是xuzhenyi手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
帮助xuzhenyi布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。

格式

输入格式

输入文件中数据表示一棵树,描述如下:
第1行 n n ,表示树中结点的数目。
第2行至第 n+1 n + 1 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 i0<in i ( 0 < i ≤ n ) ,在该宫殿安置侍卫所需的经费 k k ,该点的儿子数 m m ,接下来 m m 个数,分别是这个节点的m个儿子的标号 r1,r2,,rm r 1 , r 2 , ⋯ , r m
对于一个 n0<n1500 n ( 0 < n ≤ 1500 ) 个结点的树,结点标号在 1 1 n n 之间,且标号不重复。保证经费总和不超过 2311 2 31 − 1

输出格式

输出文件仅包含一个数,为所求的最少的经费。

样例1

样例输入1

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

样例输出1

25

提示

如图
Vijos[1144]小胖守皇宫 树动规_第1张图片

f[x][0] f [ x ] [ 0 ] 表示点 x x 的子树被控制好,并且 x x 也被控制,并且 x x 放上一个侍卫
f[x][1] f [ x ] [ 1 ] 表示点 x x 的子树被控制好,但 x x 的父亲不放人,并且 x x 不放侍卫
f[x][2] f [ x ] [ 2 ] 表示点 x x 的子树被控制好, x x 的父亲放人,并且 x x 不放侍卫

则转移方程为
f[i][0]=a[i]+Σjjif[i][0]+min(f[j][0],f[j][2]) f [ i ] [ 0 ] = a [ i ] + Σ j 是 i 的 儿 子 j f [ i ] [ 0 ] + m i n ( f [ j ] [ 0 ] , f [ j ] [ 2 ] )
意思为他的儿子可选可不选,每个儿子取个最小值即可

f[i][1]=Σjjif[i][0]+min(f[j][0],f[j][1]) f [ i ] [ 1 ] = Σ j 是 i 的 儿 子 j f [ i ] [ 0 ] + m i n ( f [ j ] [ 0 ] , f [ j ] [ 1 ] ) 且当所有的 min(f[j][0],f[j][1]) m i n ( f [ j ] [ 0 ] , f [ j ] [ 1 ] ) 都取 f[j][1] f [ j ] [ 1 ] f[i][1]+=min{f[j][0]f[j][1]} f [ i ] [ 1 ] + = m i n { f [ j ] [ 0 ] − f [ j ] [ 1 ] }
意思为他的儿子可选可不选,但至少要选上一个,如果一个都没选的话就挑一个差值最小的放上侍卫

f[i][2]=Σjjif[i][0]+min(f[j][0],f[j][1]) f [ i ] [ 2 ] = Σ j 是 i 的 儿 子 j f [ i ] [ 0 ] + m i n ( f [ j ] [ 0 ] , f [ j ] [ 1 ] )
含义同①

详见代码

代码如下:

#include
#include
#include
#define N 1550
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do{c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
int n,m,x,y,k,top;
int a[N],fir[N],f[N][3];
///0:这点放人 1:这点不放人,父节点也没放人,那么儿子就必须(划重点)要选一个 2:这点不放人,父节点放人了,所以完全OJBK
struct Edge{
    int to,nex;
    Edge(int _=0,int __=0):to(_),nex(__){}
}nex[N<<1];
inline void add(int x,int y){
    nex[++top]=Edge(y,fir[x]);
    fir[x]=top;
}
int TreeDP(int x,int k,int fa){
    if(f[x][k]) return f[x][k];
    int tmp=0;
    if(k==0){
        tmp=tmp+a[x];///咋也得加上放人的代价吧
        for(int i=fir[x];i;i=nex[i].nex){
            if(nex[i].to==fa) continue;
            tmp=tmp+min(TreeDP(nex[i].to,0,x),TreeDP(nex[i].to,2,x));///儿子放不放人都完全OJBK
        }
    }
    else if(k==1){
        int minn=2147483647;bool t=false;
        for(int i=fir[x];i;i=nex[i].nex){
            if(nex[i].to==fa) continue;
            int t1=TreeDP(nex[i].to,0,x),t2=TreeDP(nex[i].to,1,x);
            if(t1<=t2){///儿子放人居然比不放人优!
                t=true;
                tmp=tmp+t1;
            }
            else{
                tmp=tmp+t2;
                minn=min(minn,t1-t2);
            }
        }
        if(!t) tmp=tmp+minn;///如果没一个儿子放人,那就让差值较小的那个放个人
    }
    else if(k==2){
        for(int i=fir[x];i;i=nex[i].nex){
            if(nex[i].to==fa) continue;
            tmp=tmp+min(TreeDP(nex[i].to,0,x),TreeDP(nex[i].to,1,x));
        }
    }
    return f[x][k]=tmp;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        x=read();a[x]=read();k=read();
        while(k--){
            y=read();add(x,y);add(y,x);
        }
    }
    printf("%d",min(TreeDP(1,0,0),TreeDP(1,1,0)));
return 0;
}

你可能感兴趣的:(树dp,其他题库,动态规划,vijos)