树上最小点覆盖问题:消耗一个节点上的花费可以覆盖它自己及所有与它的相邻节点,求覆盖树上所有节点最小花费。
1077. 皇宫看守
太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
皇宫各个宫殿的分布,呈一棵树的形状,宫殿可视为树中结点,两个宫殿之间如果存在道路直接相连,则该道路视为树中的一条边。
已知,在一个宫殿镇守的守卫不仅能够观察到本宫殿的状况,还能观察到与该宫殿直接存在道路相连的其他宫殿的状况。
大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
输入格式
输入中数据描述一棵树,描述如下:
第一行 n,表示树中结点的数目。
第二行至第 n+1 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 i,在该宫殿安置侍卫所需的经费 k,该结点的子结点数 m,接下来 m 个数,分别是这个结点的 m 个子结点的标号 r1,r2,…,rm。
对于一个 n 个结点的树,结点标号在 1 到 n 之间,且标号不重复。
输出格式
输出一个整数,表示最少的经费。
数据范围
1≤n≤1500
输入样例:
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
样例解释:
在2、3、4结点安排护卫,可以观察到全部宫殿,所需经费最少,为 16 + 5 + 4 = 25。
每个点可以选择被儿子,自己或者父亲节点覆盖(至少被唯一次覆盖),可以用f[u][0]儿子节点防止了侍卫,f[u][1]表示自己放了侍卫,f[u][2]表示父亲放了侍卫
f[u][0]由一个子节点的f[v][1]和其他子节点的f[v][0]和[1]的最小值(没有[2]因为不能依靠父节点),遍历一遍所有子节点的可能,取一个最小值。即只要由一个子节点上放了侍卫该节点就可以被覆盖
f[u][1]由子节点的所有情况转移而来,因为该节点可以覆盖所有子节点
f[u][2]由子节点的f[v][0]和f[v][1]最小值转移而来,因为它不能覆盖子节点
#include<bits/stdc++.h>
using namespace std;
const int N=1510;
int h[N],idx,a[N],f[N][3]; //0儿子放 ,1自己放 ,2父亲放
struct eg{
int v,nex;
}e[N*2];
void add(int u,int v){
e[idx]={v,h[u]};
h[u]=idx++;
}
void dfs(int u,int pre){
f[u][1]=a[u];
//f[u][0]
int res=0;
for(int i=h[u];~i;i=e[i].nex){
int v=e[i].v;
if(v==pre) continue;
dfs(v,u);
f[u][1]+=min(f[v][0],min(f[v][1],f[v][2]));
f[u][2]+=min(f[v][1],f[v][0]);
res+=min(f[v][0],f[v][1]);
}
int ans=0x3f3f3f3f;
for(int i=h[u];~i;i=e[i].nex){
int v=e[i].v;
if(f[v][0]>=f[v][1]){//儿子自己放
ans=min(ans,res);
}
else { //儿子的儿子放
ans=min(ans,res-f[v][0]+f[v][1]);
}
//ans=min(ans,res-min(f[v][0],f[v][1])+f[v][1]);
}
f[u][0]=ans;
}
int main(){
int n;
memset(h,-1,sizeof h);
cin>>n;
for(int i=1;i<=n;++i){
int u,k,v;
scanf("%d",&u);
scanf("%d%d",&a[u],&k);
while(k--){
scanf("%d",&v);
add(u,v);
add(v,u);
}
}
if(n==1) cout<<a[1]<<endl;
else {
dfs(1,0);
cout<<min(f[1][0],f[1][1])<<endl;
}
return 0;
}