P1122 最大子树和---树形dp(超详细解答)

题目链接:
https://www.luogu.com.cn/problem/P1122
题目大意:一棵树有n个节点,每个节点都有一个权值(可正可负),现在要你找到一棵子树(也可能是整棵树本身),要求子树的所有节点权值之和最大,输出这个最大值

分析:
Step1:首先我们需要根据题中给的节点关系建立一颗树形图,注意建图的过程需要分两次,因为只告诉你a,b有边,但是不具体谁是儿子谁是爹,所以我们第一次默认编号大的为儿子,小的为爹,然后去找根节点(也就是没有爹的节点),如果根节点不止一个,此时说明我们建立的树形图是倒立的,所以反过来建图,也就是第二种建图方式,编号小的为儿子,大的为爹,找到根节点root,从根节点开始向下dfs.
Step2:dfs的过程,这个过程需要用到的就是动态规划,我们设f[u]表示以u为根节点,选择u这个节点,子树的最大权值和,g[u]表示以u为根节点,不选择u这个节点子树的最大权值和,那么最终的答案就是max(f[root],g[root]),我们知道.
1.如果选择u这个节点的话,那么子节点可选可不选,此时状态转移方程为 f [ u ] = s u m ( m a x ( f [ v ] , 0 ) ) + w [ u ] f[u] =sum(max(f[v],0))+w[u] f[u]=sum(max(f[v],0))+w[u],w[u]是u的权值,v是u子节点.
2.如果不选择u这个节点的话,那么状态转移方程 g [ u ] = m a x ( m a x ( f [ v ] , g [ v ] ) , g [ u ] ) g[u]=max(max(f[v],g[v]),g[u]) g[u]=max(max(f[v],g[v]),g[u]),注意这里的g[u]要初始化为0

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 2e4;
/*链式前向星*/
struct Edge
{
    
    
	int v;
	int next;
}edge[Maxn];
int head[Maxn];
int f[Maxn],g[Maxn];
int w[Maxn];
int a[Maxn],b[Maxn];
bool vis[Maxn];
int cnt = 0;
void build(int u,int v)
{
    
    
	edge[++cnt].v = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
	return ;
}
void dfs(int u)
{
    
    
	f[u] = w[u];
	g[u] = 0;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
    
    
		int v = edge[i].v;
		dfs(v);
		f[u]+=max(0,f[v]);
		g[u] = max(max(f[v],g[v]),g[u]);
	}
	return ;
}
int main()
{
    
    
	memset(head,-1,sizeof(head));
	memset(vis,false,sizeof(vis));
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>w[i];
	for(int i=1;i<n;i++) cin>>a[i]>>b[i];
	/*第一种建图方式*/
	for(int i=1;i<n;i++)
	{
    
    
		int u = min(a[i],b[i]);
		int v = max(a[i],b[i]);
		build(u,v);
		vis[v] = true;
	}
	int root;
	int sum = 0;
	for(int i=1;i<=n;i++)
	{
    
    
		if(!vis[i]) ++sum,root = i;
	}
	if(sum==1)
	{
    
    
		dfs(root);
		cout<<max(f[root],g[root])<<'\n';
		return 0;
	}
	/*如果第一种建图方式不满足要求*/
	else
	{
    
    
		cnt = 0;
		memset(head,-1,sizeof(head));
		memset(vis,false,sizeof(vis));
		for(int i=1;i<n;i++)
		{
    
    
		int v = min(a[i],b[i]);
		int u = max(a[i],b[i]);
		build(u,v);
		vis[v] = true;
		}
		for(int i=1;i<=n;i++)
		{
    
    
		if(!vis[i])
		{
    
    
			root = i;
			break;
		}
		}
		dfs(root);
		cout<<max(f[root],g[root])<<'\n';
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/TheWayForDream/article/details/116565272