一遍dfs
树的重心的性质就不用再说了,本题主要使用重心的概念进行解题即在所有节点为根的较大size(节点数)的子树当中找到节点数最小的节点。
本题最重要的是
理解如何实现一遍dfs递归找到最佳节点
算法的关键是使用递归操作所有的节点的子节点具体实现如下
使用vector<int>leaf[]来构建树存储节点及其相连的节点。
now表示当前的节点
son表示now的子节点
parent表示now的父节点
dp[]表示now节点的size
maxsontree表示now的son中size最大的节点数
minsize表示所有节点的最大子树中节点最少的size
number表示有几个答案
answer[]表示答案的集合
#include<iostream>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define maxn 100005
#define INF 0x3f3f3f//表示最大的数
int n,m;
vector<int>leaf[maxn];
int dp[maxn]={0};
int minsize;
int answer[10];
int number;
void dfs(int now,int parent)
{
int i,j;
dp[now]=1;//默认当前节点数为1,在最底层递归时有用(但实际上应该为0但是这样就不能进行累加)。
int maxsontree=0;
for(i=0;i<leaf[now].size();i++){
int son=leaf[now][i];
if(son!=parent){//当son不是now的父节点时继续递归
dfs(son,now);
dp[now]+=dp[son];//now的size是所有son的size的和
maxsontree=max(maxsontree,dp[son]);//选出now的所有son中最大的size
}
}
maxsontree=max(maxsontree,n-dp[now]);//举个例子:最底层的son它最大子树的size是在父节点方向的树,但是此时dp是1(实际为0),所以用总节点数减去其dp得到的是真实节点数加1,(所有的都加1结果不受影响)。
if(maxsontree<minsize){//一旦有更小的最大子树节点数
number=0;//重置答案个数。
answer[++number]=now;//存储答案
minsize=maxsontree;
}
else if(maxsontree==minsize)//有相同的答案
{
answer[++number]=now;//让答案数加1并在下一位进行存储。
}
}
int main()
{
cin>>n>>m;
int u,v,i,j;
for(i=0;i<m;i++){
scanf("%d %d",&u,&v);
leaf[u].push_back(v);
leaf[v].push_back(u);
}
minsize=INF;//开始假设最佳size数是极大的值。
number=0;//初始答案数为0.
dfs(1,0);//开始遍历
sort(answer+1,answer+number+1);//将答案排序
for(i=1;i<=number;i++){
printf("%d ",answer[i]);
}
return 0;
}
代码在递归的哪一处不太好理解,可以自己先画一个树,从最理想的情况一直递归一条边,演算一下过程,就可以明白了。