1. 程式人生 > 實用技巧 >樹的直徑&&樹形dp

樹的直徑&&樹形dp

http://acm.hdu.edu.cn/status.php
題意:給出一棵樹,求出所有結點所能到達的最遠距離。
解法一:任意一點所能到達的最遠距離一定是樹的直徑的某一端點;由樹的直徑的性質可得。

#include<bits/stdc++.h>

using namespace std ;
typedef long long ll ;
const int N = 10010 , M = 20010 ;
int e[M] , w[M] , ne[M] , h[N] , idx ;
int n ;
int p , len , d[N];//d[i]表示i結點所能到達的最遠距離

void add(int a , int b , int c){
    e[idx] = b , w[idx] = c , ne[idx] = h[a] , h[a] = idx++;
}

void dfs(int u , int pre, int dep){
    d[u] = max(d[u] , dep);
    if(dep > len){
        len = dep ;
        p = u ;
    }
    for(int i = h[u] ; ~i  ; i = ne[i]){
        int j = e[i];
        if(j == pre) continue;
        dfs(j , u ,  dep + w[i]);
    }
}
void solve(){
    memset(h , -1 , sizeof(h));
    memset(d , 0 , sizeof(d));
    p = 1 , len = 0 , idx = 0 ;
    for(int i = 2 ; i <= n ; i++){
        int a , b ;
        cin >> a >> b ;
        add(i , a , b);
        add(a , i , b);
    }
    dfs(p , 0 , 0);
    len = 0 ;
    dfs(p , 0 , 0);//直徑一段
    dfs(p , 0 , 0);//直徑另一端
    for(int i = 1 ; i <= n ; i++){
        cout << d[i] << endl;
    }
}

int main(){
    #ifdef ONLINE_JUDGE
	#else
		freopen("D:\\c++\\in.txt", "r", stdin);
		//freopen("D:\\c++\\out.txt", "w", stdout);
	#endif
    while(cin >> n)
        solve();
}

解法二:樹形dp

#include<bits/stdc++.h>

using namespace std ;
typedef long long ll ;
const int N = 10010 , M = 20010 ;
int f[N][3];//f[i][0]表示以i為子樹的離i最遠距離,f[i][1]表示以i為子樹且與最遠距離所經過的兒子不同的離i最遠距離,表示f[i][2]表示i的父節點去除i子樹的最遠距離
int e[M] , w[M] , ne[M] , h[N] , idx ;
int n ;
int maxson[N];
void add(int a , int b , int c){
    e[idx] = b , w[idx] = c , ne[idx] = h[a] , h[a] = idx++;
}

void dfs1(int u , int pre){//處理出f[i][0] , f[i][1]
    f[u][0] = f[u][1] = 0 ;
    for(int i = h[u] ; ~i  ; i = ne[i]){
        int j = e[i];
        if(j == pre) continue;
        dfs1(j , u);
        if(f[j][0] + w[i] >= f[u][0]){
            maxson[u] = j ;
            f[u][1] = f[u][0] , f[u][0] = f[j][0] + w[i] ;
        }else if(f[j][0] + w[i] > f[u][1]){
            f[u][1] = f[j][0] + w[i] ;
        }
    }
}

void dfs2(int u , int pre){//通過父節點更新兒子的f[v][2]表示v的父節點除去v子樹的最遠距離
    for(int i = h[u] ; ~i ; i = ne[i]){
        int j = e[i] ;
        if(j == pre) continue ;
        if(maxson[u] != j){
            f[j][2] = max(f[u][2] , f[u][0]) + w[i] ;
        }else{
            f[j][2] = max(f[u][2] , f[u][1]) + w[i] ;
        }
        dfs2(j , u);
    }
}

void solve(){
    memset(h , -1 , sizeof(h));
    idx = 0 ;
    for(int i = 2 ; i <= n ; i++){
        int a , b ;
        cin >> a >> b ;
        add(i , a , b);
        add(a , i , b);
    }
    dfs1(1 , 0);
    dfs2(1 , 0);
    for(int i = 1 ; i <= n ; i++){
        cout << max(f[i][0] , f[i][2]) << endl;
    }
}

int main(){
    #ifdef ONLINE_JUDGE
	#else
		freopen("D:\\c++\\in.txt", "r", stdin);
		//freopen("D:\\c++\\out.txt", "w", stdout);
	#endif
    while(cin >> n)
        solve();
}