A. The Fair Nut and the Best Path(无根树dp详解)

https://codeforces.com/problemset/problem/1083/A


题意:到达一个点得到这个点的价值,经过一个边花费这个边的价值,求得到的最大价值 


思路:之前碰过一些无根树的问题,有些是直接定1为根就可以了不用考虑其他,但是有些不行。比如树的重心,一种方法是定1为根然后考虑dp转移过程中该节点“头上”的量。比如树的直径,确实可以两次dfs求,树形dp的求法也是类似考虑节点“头上”的量。

这题是个无根树,考虑头上的量。

先考虑以u为根节点的子树,

1.u是起点,那么就是u在子树中所能获得的最大价值

2.u是转折点,那么就是u在子树中获得的最大价值与次大价值之和

这个转折点的意思是,出现了一个类似倒V形状,注意这道题的一个要求是路径是简单路径,也就是没有重边和自环

然后考虑u通过父亲所能获得的最大价值与u在子树中所能获得的最大价值之和

类似树的直径问题。

这里注意通过fa[u]获得的最大价值,是不会以u作为转折点获得的,因为转折点的话就已经完成了简单路径,要走上去的话要回走一条重边。

然后发现 u通过父亲fa所能获得的最大价值与u在子树中所能获得的最大价值之和 其实已经被

以fa为转折点(我的理解)在子树中所能获得的最大价值 所包括

而在dp的转移中,用dp[]维护的是u为起点的最大价值,怎么取u为转折点呢?用一个ans去维护ans=max(ans,dp[u]+dp[v]-cost);

这个ans的右边的值就是一个倒V的值,取且只能取一个最大的。

dp的转移也是转移以u为起点的最大价值,是一条直着的路径,不会转折。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e5+100;
typedef long long LL;
LL val[maxn],dp[maxn],ans;
vector< pair<LL,LL> >g[maxn];
void dfs(LL u,LL fa)
{
	dp[u]=val[u];
	ans=max(ans,dp[u]);
	for(LL i=0;i<g[u].size();i++)
	{
		LL v=g[u][i].first;
		LL w=g[u][i].second;
		if(v==fa) continue;
		dfs(v,u);
		ans=max(ans,dp[u]+dp[v]-w);
		dp[u]=max(dp[u],dp[v]+val[u]-w); 
	}
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n;cin>>n;
  for(LL i=1;i<=n;i++) cin>>val[i];
  for(LL i=1;i<n;i++){
  	LL x,y,w;cin>>x>>y>>w;
  	g[x].push_back({y,w});
  	g[y].push_back({x,w});
  } 
  dfs(1,0);
  cout<<ans<<endl;
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/108626342