先看题: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; }