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

12 篇文章 0 订阅
1 篇文章 0 订阅

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

描述

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

格式

输入格式

输入文件中数据表示一棵树,描述如下:
第1行 n n ,表示树中结点的数目。
第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 i0<in i ( 0 < i ≤ n ) ,在该宫殿安置侍卫所需的经费 k k ,该点的儿子数m,接下来 m m 个数,分别是这个节点的m个儿子的标号r1,r2,,rm
对于一个 n0<n1500 n ( 0 < n ≤ 1500 ) 个结点的树,结点标号在 1 1 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

提示

如图
这里写图片描述

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

则转移方程为
f[i][0]=a[i]+Σjijf[i][0]+min(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<algorithm>
#include<ctype.h>
#include<cstdio>
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值