先看題:P1395會議c++
樹的重心,就是在一棵樹中拆掉一個點,把這棵樹分紅幾個部分,使得最大的部分最小,亦即拆得均勻,這個拆掉的點就是樹的重心。spa
那麼怎麼求呢?咱們能夠從根結點開始dfs(什麼?你說是無根樹。那就定義節點1爲根唄),返回值是這棵子樹的大小。而後定義一個\(mx\),\(mx=max\{dfs(\text{該結點的子結點的})\}\) 。最後,在枚舉完子節點後,再判斷他父親那一塊的大小。而後記錄值。
如今惟一的問題就是父親那塊怎麼解決呢?沒錯,用總結點數減去這棵子樹的大小,就是他父親那一塊的大小。code
如今來解決第二問 ,這問其實能夠在解決樹的重心後直接dfs或bfs,而後就能夠得出總的路程了。這一塊就不用詳細解釋了。隊列
上代碼:ci
#include<bits/stdc++.h> using namespace std; int n,ans=50005,sum=50005,road;//n是總結點數,ans是樹的重心的編號,sum是其最那塊的大小,road是總路程 int t[50005];//ti表示結點i到ans的距離。 queue<int>q;//q用來後面bfs用 struct graph { int tot; int dt[100005],nxt[100005]; int hd[50005]; void add(int x,int y) { tot++; nxt[tot]=hd[x]; hd[x]=tot; dt[tot]=y; } }g;//graph是鏈式前向星。 int dfs(int x,int fa) { int k=0;//k表明其因此子樹的大小 int mx=0;//mx是將這個點拆掉後最大那塊的大小 for(int i=g.hd[x];i;i=g.nxt[i])//遍歷因此點子節點 if(g.dt[i]!=fa)//注意判該節點是不是x的父親,不判會爆棧的 { int xx=dfs(g.dt[i],x);//記錄這棵子樹的大小 k+=xx;//加上這棵子樹的大小 mx=max(mx,xx);//取最大值 } mx=max(mx,n-k-1);//最後判一下父親那塊,最後的-1是由於最開始沒有把本身算進去 if(mx<sum||(mx==sum&&x<ans)) sum=mx,ans=x;//若是比當前方案更優,就取這個方案 return k+1;//返回整棵子樹的大小 } void bfs() { q.push(ans);//記錄最初的點 while(!q.empty())//若是隊列不爲空 { int xx=q.front();//記錄隊首 q.pop();//彈出隊首 for(int i=g.hd[xx];i;i=g.nxt[i])//遍歷連接它的節點 { int yy=g.dt[i]; if(t[yy]||yy==ans) continue;//若是這個點已經被遍歷過,continue t[yy]=t[xx]+1;//記錄距離 road+=t[yy];//總距離要加上這個點的距離 q.push(yy);//放進隊列 } } return ; } int main() { cin>>n; for(int i=1;i<n;i++) { int from,to; cin>>from>>to; g.add(from,to); g.add(to,from); }//輸入+存圖,注意是雙向邊 dfs(1,0);//dfs,把1的父親設爲0,就是沒有父節點了(由於沒有節點0) bfs();//求出樹的重心後,求總路程 cout<<ans<<' '<<road; return 0; }