vijos1144小胖守皇宫 [树规]

来源:互联网 发布:java中final的用法 编辑:程序博客网 时间:2024/04/27 22:13

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

输入文件中数据表示一棵树,描述如下:
第1行 n,表示树中结点的数目。
第2行至第n+1n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i,在该宫殿安置侍卫所需的经费k,该点的儿子数m,接下来m个数,分别是这个节点的m个儿子的标号r1,r2,⋯,rmr1,r2,⋯,rm。
对于一个n个结点的树,结点标号在1到n之间,且标号不重复。保证经费总和不超过231−1231−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
限制
提示
如图

来源
huyichen

题意:
给出一棵树和覆盖某个点的代价,每个点被覆盖后可以覆盖与其相连的点但这个效果不再第二次延伸,求最小代价。
分析:
分析状态可以发现每个点有三种可能:被自己覆盖,被直接相连子节点覆盖或被父节点覆盖,则很容易获得转移方程(见程序),需要注意的是,对于由子节点守的情况,需要保证有取到一个f,于是这里要运用差分的思想,若到了最后f与g的差的最小值还是正数,就说明一次都没有取到过f,则需要加上差的最小值以保证此点被守到。此法顺便可以吧叶子节点的g赋值为无限大。

#include<iostream>#include<cstdio>using namespace std;const int maxn=1500+5;int n,nod,k[maxn],son[maxn][maxn],rd[maxn],rot,f[maxn],h[maxn],g[maxn];void dfs(int u){    int tmp=2e9,ok=0;    for(int i=1;i<=son[u][0];i++){        int nxt=son[u][i];        dfs(nxt);        f[u]+=min(g[nxt],min(h[nxt],f[nxt]));        g[u]+=min(f[nxt],g[nxt]);        h[u]+=min(g[nxt],f[nxt]);        tmp=min(tmp,f[nxt]-g[nxt]);    }    if(tmp<0)tmp=0;    f[u]+=k[u];g[u]+=tmp;}void init(){    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%d",&nod);        scanf("%d %d",&k[nod],&son[nod][0]);        for(int i=1;i<=son[nod][0];i++){            scanf("%d",&son[nod][i]);            rd[son[nod][i]]++;        }    }}void work(){    for(int i=1;i<=n;i++)if(!rd[i])        rot=i;    dfs(rot);    printf("%d",min(f[rot],g[rot]));}int main(){    freopen("vijos1144.in","r",stdin);    freopen("vijos1144.out","w",stdout);    init();    work();    return 0;}
0 0