题目链接
题目思路
显然就是求树的直径,但是有负边。那么两次dfs或bfs显然就不行了。此时就要用树形dp。
首先把无根树转化成有根树,然后设置dp方程,dp【i】为以i为根节点的包括i在内的最长链的长度。
dp【u】=max( dp【u】,dp【v】+a【u】)我们还要保存次长链的长度。那么最终的答案就是,枚举所有节点,把这个节点作为根,最长链加上次长链的长度减去a【u】就是最终的答案.。其实就是把这个点当作这条链的一个中转点,然后找出两条不在一颗子树的最长边。然后相加,注意要减去a【u】,因为被算了两遍。
注意
最后还要写上
dp[son]=max1;//必须要加这一句因为可能是负数,而上面的那个式子求出来dp[son]>=0
代码
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int n,u,v,a[maxn],head[maxn],cnt;
ll ans=-inf,dp[maxn];
struct node{
int to,next;
}e[maxn<<1];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int son,int fa){
ll max1=a[son],max2=a[son];//最长边和次长边
for(int i=head[son];i;i=e[i].next){
if(e[i].to!=fa){
dfs(e[i].to,son);
dp[son]=max(dp[son],dp[e[i].to]+a[son]);
if(dp[e[i].to]+a[son]>max1){
max2=max1;
max1=dp[e[i].to]+a[son];
}else if(dp[e[i].to]+a[son]>max2){
max2=dp[e[i].to]+a[son];
}
}
}
dp[son]=max1;//必须要加这一句因为可能是负数,而上面的那个式子求出来dp[son]>=0
ans=max(ans,max1+max2-a[son]);//自己重复算了一遍
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n-1;i++){
scanf("%d %d",&u,&v);
add(u,v),add(v,u);
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}