CodeForces - 1084D- The Fair Nut and the Best Path (树形dp)

解题思路转自:https://blog.csdn.net/castomere/article/details/86607780

题目链接:https://vjudge.net/problem/CodeForces-1084D

题目大意:题意是每个城市都有一个Wi 油的数量,给出了一些道路将N个城市形成一个无根树,每条道路有不同的耗油量,

 

任意一个城市x任意一个城市y ,在起始点 x 直接加 Wx 的汽油,到 y 城市最多能有多少汽油剩余量。

之前遇见无根树题目总是发懵,因为当dfs查找的时候,这个点不是查找路径的上游点,就不会对以它为转折点的权值更新。

在求 树的直径 问题时规避了这个问题,也就没有多想,直到遇见此类题目,才开始想解决问题。

那么现在就讨论和解决这个问题

首先我们假设这个树是只有一条路径的树(类似这种),我们将无根树转化为有根树(就是随便找一个点做根,当前,我们定位最左边那个点 为 树根)

1.其实向下dfs的时候,我们可以更新的是  这个节点 到  所有的子节点的时候 的最大剩余油量(可以想到 当其叶子节点时,他向下  没有了,那么最大值就是他自己)。
  2.  那么  每次返回上一层的时候,将这个节点以及它所有子节点视为一个子图,在这个子图里的所有x -> y,就都被找到了        因为我们每次更新,都是同步和 ans 取一个max的。


这个时候我们将情况变复杂,在某个节点,出现了分支结构(而且可能超过两条分支,我们要考虑普遍结构,并不是全为二叉结构)。

我们将要更新红色点,他有四个子节点,我们要计算的就是   当它作为连接某两个点   的转折点 时怎么计算

注意:如果红色点作为转折点 连接某两点  这条线路 确实是最终结果,那么就可以更新到,如果不是 也不影响最终结果

我们只需要更新,这个点   最大子节点,次大子节点    到 它本身  的sum 就好了

 

有两种写法,但其实是一种方式,我们先看一个简单易懂的,我直接截屏其他大佬的这一小部分代码

红色框是维护 最大子节点 和 次大子节点

蓝色框是维护直线式最值

绿色框就是 (本身价值 + 最大子节点价值 + 次大子节点价值)

 

那么我们来看另一个大神的精简版

绿色 是还是维护直线式的最值

白色 的部分就是 Dfs的时候 更新 最值的,当所有子节点都遍历过了,最大值和次大值必定进行了一次sum(重点)

 

附上我的AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
int cnt=0,n;
const int INF=3e5+5;
int head[INF];
struct node{
	int d;
	int di;
	int next;
}f[INF*2];
int a[INF];
ll ans,dp[INF];//dp记录的是从根节点到每个节点的最大距离。 
int vis[INF];
void init()
{
	int i,j;
	for(i=0;i<=n;i++)
	    head[i]=-1;
	cnt=0;
}
void add(int x,int y,int z)
{
	f[cnt].d =z;
	f[cnt].di =y;
	f[cnt].next =head[x];
	head[x]=cnt++;
}
void dfs(int x)
{
	vis[x]=1;
	int i,j,k;
	dp[x]=a[x];
	ans=max(ans,dp[x]);
	for(i=head[x];i!=-1;i=f[i].next )
	{
		j=f[i].di ;
		k=f[i].d ;
		if(vis[j]==0)
		{
			dfs(j);
            ans=max(ans,dp[x]+dp[j]-k);//这样处理是因为,所求的路径可能不包含根节点,ans表示的是最大值 
			dp[x]=max(dp[x],a[x]+dp[j]-k);
		}
	}
}
int main()
{
	scanf("%d",&n);
	int i,j,k,val,l;
	init();
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(i=1;i<n;i++)
	{
		scanf("%d%d%d",&j,&k,&l);
		add(j,k,l);add(k,j,l);
	}
	memset(vis,0,sizeof(vis));
	ans=0;
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}

 

引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值