牛客小白月赛22 B 树上子链 题解【模板树的直径负边】(树形dp)

题目链接

题目思路

显然就是求树的直径,但是有负边。那么两次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;
}
发布了69 篇原创文章 · 获赞 2 · 访问量 2720

猜你喜欢

转载自blog.csdn.net/m0_46209312/article/details/105364362