描述
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;
}