樹的直徑
定義:樹的直徑(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;
}