树形dp SDOI 2006 保安站岗

题意:一棵树, n n n个点,可以在 n n n个点上放保安,一个保安可以监视这个点,这个点的父节点和这个点的子节点,每一个点上有放保安的花费,在保证所有点都被监视的情况下总花费要尽可能小,求总花费。

对于这道题目,我们考虑 3 3 3种情况:
1.点 u u u被其子节点上的保安监视,我们设这种情况为 d p [ u ] [ 0 ] dp[u][0] dp[u][0]
2.点 u u u上有保安,我们设这种情况为 d p [ u ] [ 1 ] dp[u][1] dp[u][1]
3.点 u u u被其父节点上的保安监视,我们设这种情况为 d p [ u ] [ 2 ] dp[u][2] dp[u][2]

对于第二种情况,因为这个点上已经有保安了,所以三种情况均可转移,即: d p [ u ] [ 1 ] = ∑ m i n ( d p [ v ] [ 0 ] ,   d p [ v ] [ 1 ] ,   d p [ v ] [ 2 ] )   + v a l [ u ] dp[u][1]=\sum min(dp[v][0],\ dp[v][1],\ dp[v][2])\ +val[u] dp[u][1]=min(dp[v][0], dp[v][1], dp[v][2]) +val[u]

v v v为该节点的子节点, v a l i val_i vali表示在 i i i号点放置保安的花费

对于第三种情况,因为该节点已经被其父节点上的保安监视,要保证这个点的子节点被监视,所以 d p [ u ] [ 2 ] = ∑ m i n ( d p [ v ] [ 1 ] ,   d p [ v ] [ 0 ] ) dp[u][2]=\sum min(dp[v][1],\ dp[v][0]) dp[u][2]=min(dp[v][1], dp[v][0])

对于第一种情况,必须存在一个子节点上有保安来监视这个点,而其他的子节点的情况可以有保安也可以被自己的子节点上的保安监视,所以

d p [ u ] [ 0 ] = ∑ m i n ( d p [ v ] [ 0 ] ,   d p [ v ] [ 1 ] ) + ∀   d p [ v ] [ 0 ] dp[u][0]=\sum min(dp[v][0],\ dp[v][1])+\forall \ dp[v][0] dp[u][0]=min(dp[v][0], dp[v][1])+ dp[v][0]

本弱在翻看题解的时候发现了对于第一种情况非常优秀的转移方式
我们用 s u m sum sum表示 ∑ m i n ( d p [ v ] [ 1 ] ,   d p [ v ] [ 0 ] ) \sum min(dp[v][1],\ dp[v][0]) min(dp[v][1], dp[v][0]),所以我们只要把其中的一个 m i n ( d p [ v ] [ 1 ] ,   d p [ v ] [ 0 ] ) min(dp[v][1],\ dp[v][0]) min(dp[v][1], dp[v][0])替换成 d p [ v ] [ 1 ] dp[v][1] dp[v][1]然后维护最小值即可。

最后的结果就是 m i n ( d p [ 1 ] [ 1 ] ,   d p [ 1 ] [ 0 ] ) min(dp[1][1],\ dp[1][0]) min(dp[1][1], dp[1][0])

下面是代码

#include
#include
#include
using namespace std;
int n,m,k,val[1001000];
struct node
{
	int next,to;
}e[1001000];
int num,head[1001000],dp[1001000][3];
void add(int from,int to)
{
	e[++num].next=head[from];
	e[num].to=to;
	head[from]=num;
}
void dfs(int x,int fa)
{
	int sum=0;
	dp[x][1]=val[x];
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==fa)
			continue;
		dfs(v,x);
		sum+=min(dp[v][1],dp[v][0]);
		dp[x][1]+=min(dp[v][1],min(dp[v][0],dp[v][2]));
	}
	dp[x][2]=sum,dp[x][0]=999999999;
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==fa)
			continue;
		dp[x][0]=min(dp[x][0],sum-min(dp[v][0],dp[v][1])+dp[v][1]);
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&k);
		scanf("%d%d",&val[k],&m);
		for(int j=1;j<=m;++j)
		{
			int s;
			scanf("%d",&s);
			add(k,s);
			add(s,k);
		}
	}
	dfs(1,0);
	cout<<min(dp[1][1],dp[1][0]);
	return 0;
}

你可能感兴趣的:(树形DP,dp,NOIP2018)