求樹的直徑

樹的直徑

定義:樹的直徑(Diameter)是指樹上的最長簡單路。

直徑的求法:兩遍搜索 (BFS or DFS)

任選一點 w 爲起點,對樹進行搜索,找出離 w 最遠的點 u。 以 u 爲起點,再進行搜索,找出離 u 最遠的點 v。則 u 到 v的路徑長度即爲樹的直徑。:
簡單證明:
如果 w 在直徑上,那麼 u 一定是直徑的一個端點。反證:若 u 不是端點,則從直徑另一端點到 w 再到 u 的距離比直徑更長,與假設矛盾。

如果 w 不在直徑上,且 w 到其距最遠點u的路徑與直徑一定有一交點 c,當走到點 c 時,由上一個證明可知,接下來離 c
最遠的點一定是直徑某個端點,所以 u 是直徑的一個端點。

如果w不在直徑上,且w到最遠點u的路徑與直徑沒有交點,設直徑的兩端爲S與T,那麼(w->u)>(w->c)+(c->T),推出(w->u)+(S->c)+(w->c)>(S->c)+(c->T)=(S->T)與假設矛盾。

因此w到最遠點u的路徑與直徑必有交點

既然我們已經知道如何求得樹的直徑,就來做一道題試試吧
藍橋杯 大臣的旅費

注意這道題要用鄰接表去存儲,不然直接用數組來存儲的時候會wa

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 10010;
struct Node {
    int to, cost; //to是目的地,cost是路徑
}mp;
vector<Node> G[maxn];
int n, p, q, d, t, ans, now; //ans保存距離,now保持當前的位置
bool visi[maxn];
void dfs(int pos, int dis){
    if (dis > ans) {
        ans = dis;
        now = pos;
    }
    for (int i = 0; i < G[pos].size(); i++) {
        Node t = G[pos][i];
        if (t.cost && !visi[t.to]) {
            visi[t.to] = true; 
            dis += t.cost;
            dfs(t.to, dis);
            dis -= t.cost;  //遍歷每一個點,所以要回溯尋找出最遠的點
            visi[t.to] = false;
        }
    }
}
int main() {
    while (~scanf("%d", &n)) {
        t = n - 1;
        for (int i = 0; i < maxn; i++)
            G[i].clear();
        while (t--) {
            scanf("%d %d %d", &p, &q, &d);
            mp.to = q, mp.cost = d;
            G[p].push_back(mp);//p到q的距離
            mp.to = p;
            G[q].push_back(mp);//q到p的距離
        }
        memset(visi, false, sizeof(visi));
        ans = -1;
        visi[1] = true;
        dfs(1, 0);

        memset(visi, false, sizeof(visi));
        ans = -1;
        visi[now] = true;
        dfs(now, 0);

        // for (int i = 1; i <= ans; i++) 
        //     sum += i + 10;
        printf("%d\n", ans * 10 + ans * (ans + 1) / 2);
    }
    return 0;
}

參考鏈接:https://zhuanlan.zhihu.com/p/44279763?utm_source=wechat_session&utm_medium=social&utm_oi=976619319615000576

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章