1. 程式人生 > >HDU 2196 淺談樹上多源最長路動態規劃求法

HDU 2196 淺談樹上多源最長路動態規劃求法

這裡寫圖片描述
世界真的很大
樹上最長的路徑==樹的直徑
樹的直徑的求法應該都知道了,那對於樹上的每個點,求出從他出發最遠的路徑
這個當然可以每一個點DFS一遍得到,但是不如這個O(n)複雜度的演算法來的優秀

看題先:

description:

給出一棵樹,求出對於每個點,他到樹上理他最遠的點的距離

input:

多組資料,到EOF
每組資料第一行一個整數n表示節點數
接下來n-1行每行兩個整數v,w表示i+1號點到v有一條邊權為w的邊

output:

n行,每行一個整數表示答案

樹上問題一個很重要的東西就是可以限制上和下的方向
即指定根節點無根樹轉有根樹
那麼對於一個點,樹上到他的最長距離要麼是在他的子樹內,要麼就是在子樹外
記錄down為子樹內的最大距離,up為子樹外的最大距離
down可以通過一次DFS直接求到
考慮up的求法
一個點的子樹外最大距離,要麼是其父親的最大距離+邊權,要麼就是其兄弟的down最大距離+邊權
而且就算是兄弟的由於我們要求的是最大值,所以完全可以使用父親的down陣列
但是問題來了,如果父親的down正好在u的子樹內,就不能用這個來更新up了
所以我們除了往下的最大值down以外,還需要記錄一個往下的不在最大值子樹裡的最大值
分為down0,down1
注意這個down1並不是整個子樹的次大值,因為次大值有可能也和最大值在同一顆子樹裡面
記錄的是不同的子樹裡的最大值
轉移還是比較好想

完整程式碼:

#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;

struct edge
{
    int v,w,last;
}ed[400010];

int n,num=0;
int up[200010],down[200010][2],head[200010],son[200010];

void init()
{
    num=0;
    memset(head,0,sizeof(head)); 
    memset(down,0,sizeof(down));
    memset
(up,0,sizeof(up)); memset(son,0,sizeof(son)); } void add(int u,int v,int w) { num++; ed[num].v=v; ed[num].w=w; ed[num].last=head[u]; head[u]=num; } void dfs1(int u,int fa) { for(int i=head[u];i;i=ed[i].last) { int v=ed[i].v; if(v==fa) continue ; dfs1(v,u); if
(down[v][0]+ed[i].w>=down[u][0]) down[u][1]=down[u][0],down[u][0]=down[v][0]+ed[i].w,son[u]=v; else if(down[v][0]+ed[i].w>down[u][1]) down[u][1]=down[v][0]+ed[i].w; } } void dfs2(int u,int fa) { for(int i=head[u];i;i=ed[i].last) { int v=ed[i].v; if(v==fa) continue ; if(v==son[u]) up[v]=max(up[u]+ed[i].w,down[u][1]+ed[i].w); else up[v]=max(up[u]+ed[i].w,down[u][0]+ed[i].w); dfs2(v,u); } } int main() { while(scanf("%d",&n)!=EOF) { init(); for(int i=2;i<=n;i++) { int v,w; scanf("%d%d",&v,&w); add(i,v,w),add(v,i,w); } dfs1(1,1); dfs2(1,1); for(int i=1;i<=n;i++) printf("%d\n",max(down[i][0],up[i])); } return 0; } /* EL PSY CONGROO */

嗯,就是這樣