标签:
对于一个有n个结点的无根树,找一个点作为根,使得最大子树的结点数最小,换句话说,删除这个点后最大连通块的结点数最小。
任选一个点作为根,设d(i)表示以i为根的子树的结点个数,那么:
只需要一次dfs,连记忆化都不需要,因为没有重复计算。
现在重点来了:
删除结点i之后,最大连通块有多少个结点呢?
结点i的子树中最大的有max{d(j)}个结点,i的“上方子树”中有n-d(i)个结点!
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<deque>
#include<stack>
using namespace std;
const int maxn=20000+10;
vector<int>next[maxn];
int d[maxn];
int son[maxn];
int n;
void dfs(int cur,int fa){
for(int i=0;i<next[cur].size();i++){
int x=next[cur][i];
if(x==fa)continue;
dfs(x,cur);
d[cur]+=d[x];
son[cur]=max(son[cur],d[x]);//这里就记录最大子树的结点数
}
d[cur]++;
}
void maxcenter(){
int ans=n+1,id;
for(int i=1;i<=n;i++){
int Max=max(n-d[i],son[i]);
//为啥下面代码就错了?不也是求最大子树的结点数吗
// for(int j=0;j<next[i].size();j++){
// int x=next[i][j];
// if(x!=i)Max=max(Max,d[x]);
// }
if(ans>Max)ans=Max,id=i;
}
printf("%d %d\n",id,ans);
}
int main(){
int T,x,y;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)next[i].clear();
for(int i=1;i<=n-1;i++){
scanf("%d%d",&x,&y);
next[x].push_back(y);
next[y].push_back(x);
}
memset(d,0,sizeof(d));
memset(son,0,sizeof(son));
dfs(1,-1);
maxcenter();
}
return 0;
}
标签:
原文地址:http://blog.csdn.net/daniel_csdn/article/details/51331977