学习大佬:树的直径求法及证明 

定义:

一棵树的直径就是这棵树上存在的最长路径。

给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径,即直径是一个数值概念,也可代指一条路径。

求法:

一、树形dp

时间复杂度:O( n );

优点:代码量少实现方便。

不足:不容易记录路径。

实现过程:

状态:d[ x ] 以当前结点 x 为根的 子树的直径。

我们枚举每一个结点 x 以及 它要到达的下一个结点 Eiv。

树的直径的求法即相关证明【树形DP || DFS】

这两个结点所能达到的最大距离之和 加上 这两个结点的边权就有可能去更新树的直径。

即:树的直径 ans_max = max{ d[ x ] + d[ Eiv ] + edge[x, Eiv] } (1 <= x <= N)

那么 d[ x ] 通过什么更新呢?当然是由 它所连接下一个结点所能达到最大距离 来更新了;

即 d[ x ] = max{ d[ x ], d[ Eiv ] + edge[ x, Eiv ] };

核心代码:

 1 void dp(int st)
 2 {
 3     vis[st] = true;             //当前结点已访问
 4     for(int i = head[st]; i != -1; i = edge[i].nxt){
 5         int Eiv = edge[i].v;
 6         if(vis[Eiv]) continue;  //不走回头路
 7         dp(Eiv);
 8         ans_max = max(ans_max, d[st] + d[Eiv] + edge[i].w); //更新树的直径(由当前结点两段之和更新)
 9         d[st] = max(d[st], d[Eiv]+edge[i].w);                    //更新当前结点所能走的最长路径(保留较长的那边)
10     }
11 }

 

二、DFS || BFS 

时间复杂度: O( n );

优点:可以在第一次 dfs/bfs记录前驱

不足:代码量稍大

实现过程:

前提:

两次dfs或bfs。第一次任意选一个点进行dfs(bfs)找到离它最远的点,此点就是最长路的一个端点,再以此点进行dfs(bfs),找到离它最远的点,此点就是最长路的另一个端点,于是就找到了树的直径。

证明(by大佬):

假设此树的最长路径是从s到t,我们选择的点为u。反证法:假设搜到的点是v。
1、v在这条最长路径上,那么dis[u,v]>dis[u,v]+dis[v,s],显然矛盾。
2、v不在这条最长路径上,我们在最长路径上选择一个点为po,则dis[u,v]>dis[u,po]+dis[po,t],那么有dis[s,v]=dis[s,po]+dis[po,u]+dis[u,v]>dis[s,po]+dis[po,t]=dis[s,t],即dis[s,v]>dis[s,t],矛盾。
也许你想说u本身就在最长路径,或则其它的一些情况,但其实都能用类似于上面的反证法来证明的。
综上所述,你两次dfs(bfs)就可以求出最长路径的两个端点和路径长度。

核心代码:

 1 void dfs(int s)
 2 {
 3     for(int i = head[s]; i != -1; i = edge[i].nxt){
 4         int Eiv = edge[i].v;
 5         if(fa[s] == Eiv) continue; //不走回头路,也可以递归父亲结点省去fa数组空间
 6         fa[Eiv] = s;
 7         dis[Eiv] = dis[s] + edge[i].w;  //当前结点最长路径
 8         dfs(Eiv);
 9     }
10 }

 

三、举栗子

1、POJ 1985 Cow Marathon

Cow Marathon

Time Limit: 2000MS   Memory Limit: 30000K
Total Submissions: 6925   Accepted: 3279
Case Time Limit: 1000MS

Description

After hearing about the epidemic of obesity in the USA, Farmer John wants his cows to get more exercise, so he has committed to create a bovine marathon for his cows to run. The marathon route will include a pair of farms and a path comprised of a sequence of roads between them. Since FJ wants the cows to get as much exercise as possible he wants to find the two farms on his map that are the farthest apart from each other (distance being measured in terms of total length of road on the path between the two farms). Help him determine the distances between this farthest pair of farms. 

Input

* Lines 1.....: Same input format as "Navigation Nightmare".

Output

* Line 1: An integer giving the distance between the farthest pair of farms. 

Sample Input

7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S

Sample Output

52

Hint

The longest marathon runs from farm 2 via roads 4, 1, 6 and 3 to farm 5 and is of length 20+3+13+9+7=52. 

 

题意概括:

建一颗 N 个节点 M 条边的树,求树上两点最长距离(树的直径);方向都是虚的,建双向边。

解题思路:

①树形dp

AC code:

 1 //树形dp版
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <cstring>
 6 #include <vector>
 7 #define INF 0x3f3f3f3f
 8 using namespace std;
 9 const int MAXN = 5e4+5;
10 int d[MAXN];
11 bool vis[MAXN];
12 int head[MAXN], cnt;
13 int N, M, ans_max;
14 struct Edge
15 {
16     int v, w, nxt;
17     Edge(int _v = 0, int _w = 0, int _nxt = 0):v(_v), w(_w), nxt(_nxt){};
18 }edge[MAXN<<1];
19 
20 void init()
21 {
22     memset(head, -1, sizeof(head));
23     memset(d, 0, sizeof(d));
24     memset(vis, false, sizeof(vis));
25     cnt = 0;
26 }
27 
28 void AddEdge(int from, int to, int weight)
29 {
30     edge[cnt] = Edge(to, weight, head[from]);
31     head[from] = cnt++;
32 }
33 
34 void dp(int st)
35 {
36     vis[st] = true;             //当前结点已访问
37     for(int i = head[st]; i != -1; i = edge[i].nxt){
38         int Eiv = edge[i].v;
39         if(vis[Eiv]) continue;  //不走回头路
40         dp(Eiv);
41         ans_max = max(ans_max, d[st] + d[Eiv] + edge[i].w); //更新树的直径(由当前结点两段之和更新)
42         d[st] = max(d[st], d[Eiv]+edge[i].w);                    //更新当前结点所能走的最长路径(保留较长的那边)
43     }
44 }
45 
46 int main()
47 {
48     while(~scanf("%d%d", &N, &M))
49     {
50         init();
51         char ccc;
52         for(int i = 1, u, v, w; i <= M; i++){
53             scanf("%d%d%d %c", &u, &v, &w, &ccc);
54             AddEdge(u, v, w);
55             AddEdge(v, u, w);
56         }
57         //printf("%d\n", ans);
58         ans_max = 0;
59         dp(1);
60         printf("%d\n", ans_max);
61     }
62     return 0;
63 }
View Code

相关文章: