(这个题不需要换根dp,但是想当作一个换根dp的练习好了)
链接:https://ac.nowcoder.com/acm/contest/4462/B
题目描述
给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和 。
请在树 T 中找出一条最大的子链并输出。
题目思路:
(如果想AC这道题的话,直接dp出每个点的最大次大子链,就行了,因为最后答案一定是某个点的最大次大之和)
(假如我们换一下题目,要求输出树上每个点包含这个点的最大链,这就必须换根了)
涉及到换根dp了的话,首先我们假设1为根 打一个dp处理出来每个点的最大次大子链值。
dp[i][0]表示i节点往下的最大子链值,dp[i][1]表示i节点往下的次大子链值。
接下来就是换根了,假设当前某个点为根的话,那么包含他的最长链值,必定会包含他的儿子中最大值子链(因为对于这个点往外延伸出链的话,要么往下走,要么往上走,但是儿子中有一根最大的子链为什么不选呢,这里的子链可以为空)。
假如对于每个点往外延申链的话,怎么延伸呢,首先最大子链肯定选了,次大子链和往父亲方向走的链是不是挑一个最大的就行了。
这是又需要一个dp_up记录,包含这个点,往1的方向延伸的最大值。
怎么转移?
假如当前要求的点是x,他的父亲是fa
如果当前的点是他父亲的最大子链,那么,就要拿dp_up[fa]和dp[fa][1]也就是次大值比较大小转移
即 dp_up[x] = max(0ll, max(dp_up[fa],dp[fa][1]) ) + f[x];
如果这个不是父亲的最大子链,那么,就要拿dp_up[fa]和dp[fa][0]也就是最大值比较大小转移
即 dp_up[x] = max(0ll, max(dp_up[fa],dp[fa][0]) ) + f[x];
对于每个点的结果的话,就是,首先最大子链选了,再在次大子链值和dp_up[fa]中选一个就行了。
dp2记录答案 dp2[x] = dp[x][0] + max(dp[x][1] , dp_up[fa]) - f[x];
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN = 1e5+5;
vector<int>v[MAXN];
const ll inf = 1ll<<62;
ll f[MAXN];
ll dp[MAXN][2];
ll idx_Max[MAXN];
ll dp2[MAXN],dp_up[MAXN];
void dfs(ll x,ll fa)
{
ll len = v[x].size();
ll Max1 = -inf,idx = x;
ll Max2 = -inf;
for(ll i=0;i<len;i++){
ll to = v[x][i];
if(to == fa)continue;
dfs(to,x);
if(dp[to][0]>Max1){
Max1 = dp[to][0];
idx = to;
}
}
for(ll i=0;i<len;i++){
ll to = v[x][i];
if(to == fa )continue;
if(dp[to][0]>Max2 && to!=idx){
Max2 = dp[to][0];
}
}
dp[x][0] = max(0ll,Max1)+f[x];
idx_Max[x]=idx;
dp[x][1] = max(0ll,Max2)+f[x];
}
void dfs2(ll x,ll fa)
{
ll len = v[x].size();
if(x != 1){
if(idx_Max[fa] == x){
//cout<<" +++++ "<<x<<endl;
dp_up[x] = max(0ll, max(dp_up[fa],dp[fa][1]) ) + f[x];
}
else{
dp_up[x] = max(0ll, max(dp_up[fa],dp[fa][0]) ) + f[x];
}
}
dp2[x] = dp[x][0] + ( max(dp[x][1],dp_up[x]) - f[x] ) ;
for(ll i=0;i<len;i++){
ll to = v[x][i];
if(to == fa)continue;
dfs2(to,x);
}
}
int main()
{
ll n;
cin>>n;
for(ll i=1;i<=n;i++){
cin>>f[i];
}
for(ll i=1;i<n;i++){
ll a,b;
cin>>a>>b;
v[a].push_back(b);
v[b].push_back(a);
}
dfs(1,1);
dp_up[1] = f[1];
dfs2(1,1);
for(ll i=1;i<=n;i++){
cout<<i<<" : "<<dp[i][0]<<" + "<<dp[i][1]<<" * "<<idx_Max[i]<<" * "<<dp_up[i]<<endl;
}
ll Max =-inf;
for(ll i=1;i<=n;i++){
Max = max(Max,dp2[i]);
}
cout<<Max<<endl;
}
/*
5
-2 -1 -1 -2 -3
1 2
2 3
2 4
2 5
*/