树形DP个人分析(CodeForces - 1084D)

树形DP 尽量详细,

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

 

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

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

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

题目:CodeForces - 1084D

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

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

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

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

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

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

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

 

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

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

蓝色框是维护直线式最值

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

 

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

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

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

贴上大神代码(代码末端有大神原文链接,那个第一版本,就是DfS和建图方式不一样,其他的都一样,就不贴了)

还有一个重点,因为这个  转折点  更新到  ans  的过程在父节点的dfs层中 ,所以初始点1的父节点设为 0,另一个版本 1 的父节点还是 1 。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10;
#define pb push_back
#define mp make_pair 
vector<pair<ll,ll> > v[N];
int val[N],n;
ll dp[N];
ll ans;
void dfs(int u,int fa)
{
	dp[u]=val[u];
	ans=max(ans,dp[u]);
	for(int i=0;i<v[u].size();i++)
	{
		int to=v[u][i].first;
		if(to==fa) continue;
		dfs(to,u);
		ans=max(ans,dp[to]+dp[u]-v[u][i].second);
		dp[u]=max(dp[u],val[u]+dp[to]-v[u][i].second);
	}
}
int main()
{
	int x,y,z;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&val[i]);
    for(int i=1;i<n;i++)
    {
    	scanf("%d%d%d",&x,&y,&z);
    	v[x].pb(mp(y,z));
    	v[y].pb(mp(x,z));
	}
	dfs(1,0);
	printf("%lld\n",ans);
	return 0;
}
--------------------- 
作者:mmk27 
来源:CSDN 
原文:https://blog.csdn.net/mmk27_word/article/details/85001714 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值