想法:对于一棵树,对于任意一个节点,它的最大值可以来自以它为根的子树,也可以来自它的父亲。
所以需要考虑二个方向的情况。所以必须考虑2个方向。
解法:dp[i][0]保存子树的值,dp[i][1]保存父亲结点方向+dis[i,root];
可以先一遍dfs,保存任意点子树的最大值,同时保存任意点取最大值的方向,同时维护他的第二大值。
然后进行第二次dfs,对于结点u,如果它的孩子不是它取最大值路径上的点,那么孩子的值dp[i][1]为
max(dp[root][1], dp[root][0])+ dis[i,root];不然dp[i][1]为父亲结点的第二大值 + dis[i,root];
然后第二大值为dp[i][1];
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int MAXN = 10010; struct node { int to; int v; int next; }edge[MAXN*3]; int pre[MAXN],index,vis[MAXN],n; int dp[MAXN][2],way[MAXN],sv[MAXN]; //sv[]存第另一边的 不是子树的 way[]表示当前点最大值从哪里来 //dp[][0]表示子树方向的最大值 dp[][1]表示另一方向的最大值 int max(int x,int y){ return x>y?x:y; } int min(int x,int y){ return x<y?x:y; } void add(int x,int y,int z) { edge[index].to = y; edge[index].v = z; edge[index].next = pre[x]; pre[x] = index++; } void dfs1(int root) { vis[root] = 1; int i; int set = root; for(i=pre[root]; i!=-1; i=edge[i].next){ int t = edge[i].to; if(!vis[t]){ dfs1(t); if(dp[root][0] > dp[t][0] + edge[i].v){ sv[root] = max(sv[root], dp[t][0] + edge[i].v); } else { set = t; sv[root] = dp[root][0]; dp[root][0] = dp[t][0] + edge[i].v; } } } way[root] = set; } void dfs2(int root) { vis[root] = 1; int i; for(i=pre[root]; i!=-1; i=edge[i].next){ int t = edge[i].to; if(!vis[t]){ if(way[root] != t){ dp[t][1] = max(dp[root][0], dp[root][1]) + edge[i].v; sv[t] = dp[t][1]; } else { dp[t][1] = sv[root] + edge[i].v; sv[t] = dp[t][1]; } dfs2(t); } } } int main() { int i; while(~scanf("%d",&n)) { index = 1; memset(sv,0,sizeof(sv)); memset(dp,0,sizeof(dp)); memset(pre,-1,sizeof(pre)); for(i=2; i<=n; ++i){ int x,v; scanf("%d %d",&x, &v); add(i, x, v); add(x, i, v); } memset(vis,0,sizeof(vis)); dfs1(1); /*for(i=1; i<=n; i++){ printf("%d ",sv[i]); } cout<<endl;*/ memset(vis,0,sizeof(vis)); dfs2(1); for(i=1; i<=n; i++){ printf("%d\n",max(dp[i][0],dp[i][1])); } } } /* 7 1 1 1 1 2 1 2 1 3 1 3 1 8 1 1 1 1 2 1 2 1 3 1 5 1 7 1 */
时间: 2024-10-16 22:51:24