题意:给定一棵树,要求按编号从小到大输出重心。
树的重心:
定义:找到一个点满足其所有的子树中最大的子树节点数最少,那么这个点就算是树的重心。
性质:
1.删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心;
2.树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;
3.两个树通过一条边合并,新的重心在原树两个重心的路径上;
4.树删除或添加一个叶子节点,重心最多只移动一条边;
DFS求树的重心:
设son[i]:以i为根的子树,孩子节点数目
son[rt]=Σ(son[child]+1) (+1是因为要加上孩子节点自己的点权)
回溯的时候更新son[rt],并且比较rt的所有子树
//#pragma comment(linker, "/STACK:102400000,102400000")//手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f3f;
const double eps=1e-5;
const int maxn=5e4+7;
//树的重心:找到一个点,其所有的子树中最大的子树节点数最少,
//那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
//son[i]记录i为根的树,孩子节点数目
struct
{
int to,next;
}edge[maxn<<1];
int head[maxn],cnt=1;
int n;
int son[maxn];
int size=INF;//存重心、最大子树节点数目
int mmin=INF;
set<int> st;
void add(int from,int to)
{
edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt++;
}
void dfs(int rt ,int par)
{
int max_son=-1;//最大子树的节点数目
for(int i=head[rt];i;i=edge[i].next)
{
int to=edge[i].to;
if(to==par) continue;
dfs(to,rt);
son[rt]+=son[to]+1;
max_son=max(max_son,son[to]+1);
}
max_son=max(max_son,n-1-son[rt]);//考虑父节点所在子树数目,n-1是减去rt自身
if(max_son<mmin)
{
st.clear();
st.insert(rt);
mmin=max_son;
}
else if(max_son==mmin)
{
st.insert(rt);
}
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int v,u;
scanf("%d%d",&v,&u);
add(u,v),add(v,u);
}
dfs(1,1);
for(set<int>::iterator it=st.begin();it!=st.end();it++)
{
printf("%d ",*it);
}
system("pause");
return 0;
}