求樹的直徑(兩種方法)


① 兩次dfs

方法:先從任意一點P出發,找離它最遠的點Q,再從點Q出發,找離它最遠的點W,W到Q的距離就是是的直徑

證明如下:

①若P已經在直徑上,根據樹的直徑的定義可知Q也在直徑上且為直徑的一個端點

②若P不在直徑上,我們用反證法,假設此時WQ不是直徑,AB是直徑

--->若AB與PQ有交點C,由於P到Q最遠,那么PC+CQ>PC+CA,所以CQ>CA,易得CQ+CB>CA+CB,即CQ+CB>AB,與AB是直徑矛盾,不成立,如下圖(其中AB,PQ不一定是

直線,畫成直線是為了方便):

--->若AB與PQ沒有交點,M為AB上任意一點,N為PQ上任意一點。首先還是NP+NQ>NQ+MN+MB,同時減掉NQ,得NP>MN+MB,易知NP+MN>MB,所NP+MN+MA>MB+MA,

即NP+MN+MA>AB,與AB是直徑矛盾,所以這種情況也不成立,如下圖:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 100000
 6 using namespace std;
 7 inline int read() 
 8 {
 9     int x=0;
10     bool f=1;
11     char c=getchar();
12     for(; !isdigit(c); c=getchar()) if(c=='-') f=0;
13     for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0';
14     if(f) return x;
15     return 0-x;
16 }
17 struct node
18 {
19     int u,v,w,nex;
20 }edge[2*maxn+10];
21 int n,m,d[maxn+10],head[maxn+10],f_num,cnt=0,ans;
22 inline void add(int x,int y,int z)
23 {
24     cnt++;
25     edge[cnt].u=x;
26     edge[cnt].v=y;
27     edge[cnt].w=z;
28     edge[cnt].nex=head[x];
29     head[x]=cnt;
30 }
31 inline void dfs(int x,int fa)
32 {
33     if(ans<d[x])
34     {
35         ans=d[x];
36         f_num=x;
37     }
38     for(int i=head[x];i!=-1;i=edge[i].nex)
39     {
40         int j=edge[i].v;
41         if(j==fa)continue;
42         d[j]=d[x]+edge[i].w;    
43         dfs(j,x);
44     }
45 }
46 int main()
47 {
48     memset(head,-1,sizeof(head));
49     n=read();m=read();
50     for(int i=1;i<=m;i++)
51     {
52         int x,y,z;
53         x=read();y=read();z=read();
54         add(x,y,z);
55         add(y,x,z);
56     }
57     dfs(1,0);
58     ans=0;
59     d[f_num]=0;
60     dfs(f_num,0);
61     printf("%d",ans);
62     return 0;
63 }

② 樹形DP

對於每個節點我們要記錄兩個值:f1 [ i ] 表示以 i 為根的子樹中,i 到葉子結點距離的最大值f2 [ i ] 表示以 i 為根的子樹中,i 到葉子結點距離的次大值對於一個節點,它到葉子結點距

離的最大值和次大致所經 過的路徑肯定是不一樣的若j是i的兒子,那么(下面的 w [ i ][ j ] 表示 i 到 j 的路徑長度):

若 f1 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [ i ] =f1 [ i ],f1 [ i ] = f1 [ j ] + w [ i ][ j ];

否則,若 f2 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [i ] = f1 [ j ] + w [ i ][ j ]; 

理解:這樣做就是,先看能否更新最大值,若能,它的次大值就是原先的最大值,再更新它的最大值;若不能,就看能不能更新次大值,若能,就更新,不能就不管它

這樣的話,最后的答案 answer = max { f1 [ i ] + f2[ i ] }

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 100000
 6 #define INF 2147483647/2-1
 7 using namespace std;
 8 inline int read() 
 9 {
10     int x=0;
11     bool f=1;
12     char c=getchar();
13     for(; !isdigit(c); c=getchar()) if(c=='-') f=0;
14     for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0';
15     if(f) return x;
16     return 0-x;
17 }
18 int n,m,ans,f1[maxn+10],f2[maxn+10],head[maxn+10],cnt=0;
19 struct node
20 {
21     int u,v,w,nex;
22 }edge[2*maxn+10];
23 inline void add(int x,int y,int z)
24 {
25     cnt++;
26     edge[cnt].u=x;
27     edge[cnt].v=y;
28     edge[cnt].w=z;
29     edge[cnt].nex=head[x];
30     head[x]=cnt;
31 }
32 inline void dp(int x,int fa)
33 {
34     for(int i=head[x];i!=-1;i=edge[i].nex)
35     {
36         int j=edge[i].v;
37         if(j==fa)continue;
38         dp(j,x);
39         if(f1[x]<f1[j]+edge[i].w)
40         {
41             f2[x]=f1[x];
42             f1[x]=f1[j]+edge[i].w;
43         }
44         else if(f2[x]<f1[j]+edge[i].w)
45         {
46             f2[x]=f1[j]+edge[i].w;
47         }
48         ans=max(ans,f1[x]+f2[x]);
49     }
50 }
51 int main()
52 {
53     memset(head,-1,sizeof(head));
54     n=read();m=read();
55     for(int i=1;i<=m;i++)
56     {
57         int x,y,z;
58         x=read();y=read();z=read();
59         add(x,y,z);
60         add(y,x,z);
61     }
62     dp(1,0);
63     printf("%d",ans);
64     return 0;
65 }
原文作者:forever_dreams 來源:CSDN
請各位大佬斧正(反正我不認識斧正是什么意思)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM