解题思路
个节点被覆盖只有三种可能:设fx,0/1/2f_{x,0/1/2}fx,0/1/2 分别表示被父亲/孩子/自己/覆盖,可以得到转移方程:
-
fx,0=∑fsonx,0f_{x,0}=∑f_{son_x,0}fx,0=∑fsonx,0
(因为他被自己的父亲覆盖,所以它本身及它的子节点都不能选,若选了儿子,就会被儿子再覆盖,故子节点只有000的状态) -
fx,1=min(fsonx,1−min(fsonx,1,fsonx,0))+∑min(fsonx,0,fsonx,1)f_{x,1}=min(f_{son_x,1}-min(f_{son_x,1},f_{son_x,0}))+∑min(f_{son_x,0},f_{son_x,1})fx,1=min(fsonx,1−min(fsonx,1,fsonx,0))+∑min(fsonx,0,fsonx,1)
(因为被儿子覆盖,有一个儿子必选。所以先把所有儿子的最优情况算出来,然后枚举那个要选的儿子,若那个儿子是fy,1f_{y,1}fy,1,就要用fy,2f_{y,2}fy,2替换) -
fx,2=rx+∑min(fsonx,0,fsonx,1,fsonx,2)f_{x,2}=r_x+∑min(f_{son_x,0},f_{son_x,1},f_{son_x,2})fx,2=rx+∑min(fsonx,0,fsonx,1,fsonx,2)
(子节点随便选)
最终答案即为 min(fx,1,fx,2min(f_{x,1},f_{x,2}min(fx,1,fx,2) )(因为根没有父节点)
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
long long f[2000][3],r[2000],n,x,y,k,m,head[3100];
struct c{
int x,next;
}a[3100];
void add(int x,int y)
{
a[++k].x=y;
a[k].next=head[x];
head[x]=k;
}
void dfs(int dep,int fa){
long long d=0;
f[dep][1]=1e9,f[dep][2]=r[dep];
for(int i=head[dep];i;i=a[i].next)
{
int y=a[i].x;
if(y==fa)continue;
dfs(y,dep);
f[dep][0]+=f[y][1];
d+=min(f[y][1],f[y][2]);
f[dep][2]+=min(f[y][2],min(f[y][1],f[y][0]));
}
for(int i=head[dep];i;i=a[i].next)
{
int y=a[i].x;
if(y==fa)continue;
f[dep][1]=min(f[dep][1],d-min(f[y][2],f[y][1])+f[y][2]);
}
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&x);
scanf("%lld%lld",&r[x],&m);
for(int j=1;j<=m;j++)
{
scanf("%lld",&y);
add(x,y);
add(y,x);
}
}
dfs(1,0);
printf("%lld",min(f[1][1],f[1][2]));
return 0;
}
/*
9
2 16 3 1 3 4
1 8000 3 5 6 7
3 6000 1 8
4 8000 0
5 1 1 9
6 900 0
7 100 0
8 2 0
9 20 0
*/