\[\large 扯 :\]算法
作 P2491 [SDOI2011]消防 的時候 ,
在尋找 距離每一個點的最遠點時卡住了 = =
在 題解的幫助 不懈的努力 下 , 獲得了 :
距離每一個點的最遠點 必定爲直徑的 一端點 的結論
\(\text{sb}\ \text{Luckyblock}\) 並不會證 , 因而就有了這篇文章 :spa
\[\large 實現 :\]code
隨意選擇 一節點爲根 .
對於每個 樹上節點 \(u\) ,
維護 其子樹中 以其爲端點 的 最長鏈\(\text{F1}_u\) 與 次長鏈\(\text{F2}_u\) ,blog
顯然 , 樹的直徑 \(=\large \max\limits_{u=1}^{n}{(\text{F1}_u + \text{F2}_u)}\)
必然有一 直徑上的點 , 其 最長鏈 與 次長鏈 鏈接後剛好爲直徑遞歸
\[\large 優缺點:\]get
優勢 : 簡單好寫
缺點 : 不易記錄 樹的直徑的路徑it
\[\large 代碼:\]class
int dfs1(int u, int fa) { int marx = 0, marx2 = 0;// 最長鏈,次長鏈 for(int i = head[u]; i; i = e[i].ne) if(e[i].v != fa) { int ret = dfs1(e[i].v, u) + e[i].w;//子節點傳回的最長鏈 if(ret >= marx) {marx2 = marx , marx = ret;}//維護 最長鏈,次長鏈 } ans = max(ans, (marx+marx2));//樹上最長路徑= max(最長鏈+次長鏈) return marx;//遞歸回傳最長鏈 }
雖然樹形 \(\text{DP}\) 很是優秀 \(\dots\)
可是它並非 此文的主角map
\[\large 實現 : \]im
首先 , 隨意選擇 一個點 爲根 ,
經過 \(\text{DFS}\) 求得樹上其餘點 , 到達此點的距離
距離其 最遠的點 , 必然爲直徑的 一端點
以上一步中 找到的直徑的端點 爲根 ,
再經過 \(\text{DFS}\) 求得樹上其餘點 , 到達此點的距離
距離其最遠的點 , 必然爲直徑的 另外一端點
\[\large 證實 :\]
若證得 : 距每一個點最遠的點 定爲直徑的 一端點 , 便可證實上述算法的正確性
設直徑兩端點 分別爲 : \(x_1 與 x_2\) ; \(d(x,y)\) 爲樹上兩點之間 的 距離
設 \(u\) 與非直徑上的點 中距離最遠的 點 爲 \(y\)
若 \(u\) 在直徑上 :
設 \(d(u,x_1) > d(u,x_2)\) ,
顯然 , 有 \(d(u,x_1) > d(u,y)\)
能夠反證 :
若 \(d(u,y) > d(u,x_1)\) , 則 \(d(u,x1) + d(u,y) > d(u,x1) + d(u,x2)\)
則應選擇 \(x_1\) 與 \(y\) 做爲 直徑 , 而非 \(x_1\) 與 \(x_2\) ,
反證 結論正確 , 距 \(x\) 最遠的點應爲 \(x1\) , 而非 \(y\)
若 \(u\) 不在直徑上 且 路徑 \((u,y)\) 與 路徑\((x_1,x_2)\) 有交點 \(z\) :
設 \(d(z,x_1) > d(z,x_2)\) ,
顯然 , 有 \(d(z,x_1) > d(z,y)\)
不然 不知足樹的直徑的性質
則有 : \(d(u,z) + d(z,x1) > d(u,z) + d(z,y)\)
故: 距 \(x\) 最遠的點應爲 \(x1\) , 而非 \(y\)
若 \(u\) 不在直徑上 且 路徑 \((u,y)\) 與 路徑\((x_1,x_2)\) 無交點 :
設路徑 \((x,y)\) 上一點 \(z1\) 與 直徑上一點 \(z2\) 經過路徑 \((z1,z2)\) 相連通
設 \(d(x2,z2) > d(x1,z2)\)
顯然 , 有 : \(d(x2,z2) > d(z1,z2) + d(y,z1)\)
不然 不知足樹的直徑的性質
則有 : \(d(u,z1) + d(z1,z2) +d(x2,z2) > d(u,z1) + d(z1,y)\)
故: 距 \(u\) 最遠的點應爲 \(x2\) , 而非 \(y\)
\(\text{Q.E.D}\) [495...]
\[\large 代碼:\]
void dfs(int now,int fat,int sum,bool flag)//dfs求得 樹的直徑 { if(flag) pre[now] = fat, map[now] = sum;//第二次dfs記錄路徑 (前驅 dis[now] = dis[fat] + sum;//更新距離 for(int i = head[now]; i; i = e[i].ne) if(e[i].v != fat) dfs(e[i].v,now,e[i].w,flag); } void get_road()//求得 樹的直徑 { dfs(1,0,0ll,0); //一次dfs for(int i = 1, maxdis = 0; i <= n; i ++)//選擇 距離最遠的點 if(dis[i] > maxdis) u = i,maxdis = dis[i]; dfs(u,0,0ll,1); //二次dfs for(int i = 1, maxdis = 0; i <= n; i ++)//選擇 距離最遠的點 if(dis[i] > maxdis) v = i,maxdis = dis[i]; }