樹上字首和學習應用筆記——樹的直徑
阿新 • • 發佈:2020-08-10
By pjx,拿走請附上連結
樹的直徑
0、前言
這是幾個月前寫了一半的東西好大一口鍋,填個坑
*如果沒有學過書上字首和,建議先閱讀該文獻
一、定義
一些無向邊且權值均為正整數構成的樹上(可以無根),距離最長的2個點的距離為樹的直徑。
二、解法(邊權為例)
1、思路
先任意從一點\(a\) 出發,找到離它最遠的那個點\(b\);
再從\(b\) 出發,找到離\(b\) 最遠的點\(c\);
則\(b\) 到\(c\) 的距離即為樹的直徑。
下面給出證明:
2、證明
不難得證,因為樹上每兩點間只可能有\(1\)條路徑,長度都唯一,
分情況討論:
1、如果\(a\) 點就為樹的直徑的起始點,則找到的\(b\)
2、如果\(a\) 不為樹直徑的兩個端點,則\(b\) 離\(a\) 最遠。如果\(b\) 不為直徑的端點,那麼一定有一個點比\(b\) 點遠,那個點就是直徑的端點。(因為為正整數權值,\(b\) 連向直徑的端點那一段路也會讓權值變得更大)
綜上,\(b\) 便為直徑的一個端點,與\(b\) 點最遠的點就一定是另一個端點了。
得證。
3、樹上字首和 + DFS大法吼啊
如何找出最遠的點呢?字首和是個好東西。先跑一邊從\(a\) 開始的字首和,找到 \(b\),從\(b\) 跑字首和,找到\(c\), 輸出字首和那個值就行了。
三、code:
#include <iostream> #include <cstdlib> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <queue> using namespace std; const int N = 10005; int b[N],cnt,n,m,head[N],total[N],total2[N],b2[N]; struct node{ int u,v,w,next; }ed[N]; void add_edge(int u,int v,int w)//鄰接表存圖 { cnt++; ed[cnt].u=u; ed[cnt].v=v; ed[cnt].w=w; ed[cnt].next=head[u]; head[u]=cnt; } void dfs(int xx)//第一次字首和 { //前面沒有賦自己的值,不要弄混兩個字首和 for(int i=head[xx];i!=0;i=ed[i].next) { int temp=ed[i].v; if(!b[temp]) { b[temp]=1; total[temp]=ed[i].w+total[xx]; dfs(temp); } } } void dfs2(int xx)//這是第二次字首和 { for(int i=head[xx];i!=0;i=ed[i].next) { int temp=ed[i].v; if(!b2[temp]) { b2[temp]=1; total2[temp]=ed[i].w+total2[xx]; dfs2(temp); } } } int main() { int m; cin>>n>>m; for(int i=1;i<=n-1;i++) { int x,y,k; cin>>x>>y>>k; add_edge(x,y,k); add_edge(y,x,k); } b[1]=1; dfs(1);//第一遍,從a找到b int root,maxn=-1; for(int i=1;i<=n;i++) { if(total[i]>maxn)//找出最大值 { maxn=total[i]; root=i; } } b2[root]=1; dfs2(root);//第二遍,從b再找到c int root2,maxn2=-1; for(int i=1;i<=n;i++) { if(total2[i]>maxn2)//找出最大值 { maxn2=total2[i]; root2=i; } } cout<<maxn2; return 0; }