樹的直徑

樹的直徑

\[\large 扯 :\]算法

P2491 [SDOI2011]消防 的時候 ,
在尋找 距離每一個點的最遠點時卡住了 = =
題解的幫助 不懈的努力 下 , 獲得了 :
距離每一個點的最遠點 必定爲直徑的 一端點 的結論
\(\text{sb}\ \text{Luckyblock}\) 並不會證 , 因而就有了這篇文章 :spa

樹形 \(\text{DP}\)

\[\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


兩次 \(\text{DFS}\)

\[\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];
}
相關文章
相關標籤/搜索