题解【没有上司的舞会】

期末考前写题解,(rp++)

[description ]

给出一棵点带权树,你需要选取若干个点:

(<1> :) 选取的点两两不具有父子关系

(<2> :) 在满足 (<1>) 的情况下最大化点权和

求最大的点权和。

[solution ]

树形 (dp) 经典。

f[u][0] 表示在不选 (u) 的情况下,在以 (u) 为根的子树中取点的最大点权和。

f[u][1] 表示在选取 (u) 的情况下,在以 (u) 为根的子树中取点的最大点权和。

若不选 (u) ,则对于 (u) 的任意一个儿子,取不取都不会与 (u) 形成父子关系,则有状态转移方程:

[f[u][0]=sum_{v=operatorname{son}(u)}operatorname{max}(f[v][0],f[v][1]) ]

若选取 (u) ,则不能选取 (u) 的任意一个儿子,则有状态转移方程:

[f[u][1]=val[u]+sum_{v=operatorname{son}(u)}f[v][0] ]

答案为 (operatorname{max}(f[root][0],f[root][1]))

一次树形 (dp) 即可解决。

[code ]

#include<cstdio>
#include<algorithm>

#define RI register int

using namespace std;

inline int read()
{
	int x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
	while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
	return x*f;
}

const int N=6100,M=6100;

int n;

int val[N];

bool sig[N];

int tot,head[N],ver[M],Next[M];

void add(int u,int v)
{
	ver[++tot]=v;    Next[tot]=head[u];    head[u]=tot; 
}

int f[N][2];

void dp(int u)
{
	f[u][0]=0;
	f[u][1]=val[u];
	for(RI i=head[u];i;i=Next[i])
	{
		int v=ver[i];
		dp(v);
		f[u][0]+=max(f[v][0],f[v][1]);
		f[u][1]+=f[v][0];
	}
}

int main()
{
	n=read();

	for(RI i=1;i<=n;i++)
		val[i]=read();

	for(RI i=1;i<n;i++)
	{
		int u=read(),fa=read();
		sig[u]=true,add(fa,u);
	}

	read(),read();

	int root;
	for(RI i=1;i<=n;i++)
	if(sig[i]==false)
	{
		root=i;
		break;
	}

	dp(root);

	printf("%d
",max(f[root][0],f[root][1]));

	return 0;
}

[thanks for watching ]

原文地址:https://www.cnblogs.com/cjtcalc/p/12216599.html