描述
huyichen世子事件后,xuzhenyi成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是xuzhenyi手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
帮助xuzhenyi布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
格式
输入格式
输入文件中数据表示一棵树,描述如下:
第1行
n
n
,表示树中结点的数目。
第2行至第行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号
i(0<i≤n)
i
(
0
<
i
≤
n
)
,在该宫殿安置侍卫所需的经费
k
k
,该点的儿子数,接下来
m
m
个数,分别是这个节点的m个儿子的标号 。
对于一个
n(0<n≤1500)
n
(
0
<
n
≤
1500
)
个结点的树,结点标号在
1
1
到之间,且标号不重复。保证经费总和不超过
231−1
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
x
的子树被控制好,但的父亲不放人,并且
x
x
不放侍卫
表示点
x
x
的子树被控制好,的父亲放人,并且
x
x
不放侍卫
则转移方程为
①
意思为他的儿子可选可不选,每个儿子取个最小值即可
②
f[i][1]=Σjj是i的儿子f[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]=Σjj是i的儿子f[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;
}