test 宝藏(树形dp)

题解 :树形dp

f[i]表示从i出发只能向下走最终回到i的最大值,f1[i]表示从i出发只能向下走停在某颗子树的最大值,

f2[i]表示从i出发只能向下走停在某颗子树的次大值

g[i]表从i出发只能向上最终回到i的最大值,g1[i]表示从i出发只能向上最终不回到i的最大值。

然后正反两遍dfs就可以了,写的时候有很多细节需要处理,用f更新g的时候要注意有些点的权值和边权没有加入f,所以计算的时候也不用减去。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 300003
using namespace std;
int n,m,f[N],g[N],f1[N],f2[N],mark[N],g1[N];
int point[N],next[N*2],v[N*2],c[N*2],val[N],tot;
void add(int x,int y,int  z)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void dfs(int x,int fa)
{
	f2[x]=f1[x]=f[x]=val[x]; 
	int maxn=0; int maxx=0; int maxn1=0; int maxx1=0;
	int size=0;
	for (int i=point[x];i;i=next[i]){
		if (v[i]==fa) continue;
		dfs(v[i],x); size++;
		g[v[i]]=f[v[i]]-2*c[i];
		if (g[v[i]]>0)  
		{
		 f[x]+=g[v[i]]; mark[v[i]]=0;
		 int t=f1[v[i]]-f[v[i]]+c[i];
		 if (t>=maxn)  
		   maxx1=maxx,maxn1=maxn,maxx=v[i],maxn=t;
		 else if (t>maxn1) maxn1=t,maxx1=v[i];
	    }
	    else if (f1[v[i]]-c[i]>=maxn)  
		       maxx1=maxx,maxn1=maxn,maxn=f1[v[i]]-c[i],maxx=v[i];
		     else if (f1[v[i]]-c[i]>maxn1) maxn1=f1[v[i]]-c[i],maxx1=v[i];
	}
	f1[x]=f[x]+maxn; mark[maxx]=1;
	f2[x]=f[x]+maxn1; mark[maxx1]=2;
}
void dfs1(int x,int fa)//f 能加的都加了,f1加入了不返回的最大值,f2中加入了不返回的次大值。 
{
	for (int i=point[x];i;i=next[i]){
		if (v[i]==fa) continue;
		int t=0;
		if (mark[v[i]]==1) {
			int t=0;
			if (f[v[i]]-2*c[i]>0)  t=-f[v[i]]+c[i];
			g[v[i]]=max(0,g[x]+f[x]+t+(t==0?-2*c[i]:-c[i]));
			g1[v[i]]=max(g[x]+f2[x]+t+(t==0?-c[i]:0),g1[x]+f[x]+t+(t==0?-c[i]:0));
		}
		else 
		if (mark[v[i]]!=-1) {
		    int t=0;
		    if (f[v[i]]-2*c[i]>0) t=-f[v[i]]+c[i];
		    g[v[i]]=max(0,g[x]+f[x]+t+(t==0?-2*c[i]:-c[i]));
		    g1[v[i]]=max(g[x]+f1[x]+t+(t==0?-c[i]:0),g1[x]+f[x]+t+(t==0?-c[i]:0));
		}
		else {
			g[v[i]]=max(0,g[x]+f[x]-2*c[i]);
			g1[v[i]]=max(0,max(g[x]+f1[x]-c[i],g1[x]+f[x]-c[i]));
		}
		dfs1(v[i],x);
	}
}
int main()
{
	freopen("treasure.in","r",stdin);
	freopen("treasure.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<n;i++){
		int x,y,z; scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	memset(mark,-1,sizeof(mark));
	for (int i=1;i<=n;i++) scanf("%d",&val[i]);
	dfs(1,0); 
	memset(g,0,sizeof(g));
	dfs1(1,0);
	//for (int i=1;i<=n;i++) 
	// cout<<f[i]<<" "<<f1[i]<<" "<<f2[i]<<" "<<g[i]<<" "<<g1[i]<<endl;
	for (int i=1;i<=n;i++)
	 {
	 	int t=max(f[i]+g1[i],f1[i]+g[i]);
	 	if (i==1)  printf("%d\n",f1[i]);
		else printf("%d\n",t);
	 }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值