题目链接:https://vijos.org/p/1144
huyichen世子事件后,xuzhenyi成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是xuzhenyi手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
帮助xuzhenyi布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
输入文件中数据表示一棵树,描述如下:
第1行 n n ,表示树中结点的数目。
第2行至第 n+1 n + 1 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 i(0<i≤n) i ( 0 < i ≤ n ) ,在该宫殿安置侍卫所需的经费 k k ,该点的儿子数 m m ,接下来 m m 个数,分别是这个节点的m个儿子的标号 r1,r2,⋯,rm r 1 , r 2 , ⋯ , r m 。
对于一个 n(0<n≤1500) n ( 0 < n ≤ 1500 ) 个结点的树,结点标号在 1 1 到 n n 之间,且标号不重复。保证经费总和不超过 231−1 2 31 − 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
25
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]+Σjj是i的儿子f[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]=Σ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
#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;
}