2020年8月17日 面向物件的高階特性
阿新 • • 發佈:2020-08-17
先看題:P1395會議
樹的重心,就是在一棵樹中拆掉一個點,把這棵樹分成幾個部分,使得最大的部分最小,亦即拆得均勻,這個拆掉的點就是樹的重心。
那麼怎麼求呢?我們可以從根結點開始dfs(什麼?你說是無根樹。那就定義節點1為根唄),返回值是這棵子樹的大小。然後定義一個\(mx\),\(mx=max\{dfs(\text{該結點的子結點的})\}\) 。最後,在列舉完子節點後,再判斷他父親那一塊的大小。然後記錄值。
現在唯一的問題就是父親那塊怎麼解決呢?沒錯,用總結點數減去這棵子樹的大小,就是他父親那一塊的大小。
現在來解決第二問 ,這問其實可以在解決樹的重心後直接dfs或bfs,然後就可以得出總的路程了。這一塊就不用詳細解釋了。
上程式碼:
#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; }