题目大意:
给你一颗树,定义树的中心为:如果去掉某个结点,那么剩下的若干子树中,结点最多的树的结点个数就是某结点的价值,计算所有的点的价值,那么价值最小的点就是树的中心。中心可以存在好多个。要你求那个最小的价值,中心的个数,并把这些中心点的编号按照升序输出。
解题思路:
用一遍dfs就搞定了,从第一个点开始往下遍历,如果没有访问过就去访问。sum[i]指从第一个点往下遍历的方向上,以i为根的子树的结点数之和并加上自己这个点(如果点k是树的最头上的一个点,那么sum[k]=N),之前看别人的博客看不懂为啥要整这个sum,然后恍然大悟,因为你要算某个点的价值的时候,你要比较这个点的子树,取个最大值,然后再用取出来的这个最大值跟自己这个点上面的结点之和比较,也就是跟N-sum[i]比较(i为正在计算价值的点),比较完得出的结果就是ans[i],所有的ans[i]里的minvalue就是要求的答案。然后再遍历一遍,就ok了。O(n)。
感想:
一道老李在vj上贴的题,一开始不会做,看了别人的博客才会的,唉,自己太水,要多多努力才行。但是ac了依然很开心,我感觉看不懂别人的代码和思路,还是自己的看的顺眼(貌似每个人都是这样),不管怎样,a了就行。
Sample Input
7 1 2 2 3 2 4 1 5 5 6 6 7
Sample Output
3 1 1
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <stack>
using namespace std;
typedef long long LL;
const int INF=0x7fffffff;
const int MAX_N=16009;
int N,minvalue=INF;
vector<int> e [MAX_N];
bool vis[MAX_N];
int sum[MAX_N];
int ans[MAX_N];
int numlist[MAX_N];
int dfs(int a){
int num=1;
int M=0;
vis[a]=1;
for(int i=0;i<e[a].size();i++){
if(!vis[e[a][i]]){
num+=dfs(e[a][i]);
M=max(sum[e[a][i]],M);
}
}
sum[a]=num;
ans[a]=max(M,N-num);
minvalue=min(minvalue,ans[a]);
return num;
}
int main(){
cin>>N;
int a,b;
memset(vis,0,sizeof(vis));
for(int i=1;i<N;i++){
scanf("%d%d",&a,&b);
e[a].push_back(b);
e[b].push_back(a);
}
dfs(1);
int ct=0;
// cout<<minvalue<<endl;
for(int i=1;i<=N;i++){
if(ans[i]==minvalue){
numlist[ct]=i;
ct++;
}
}
// sort(numlist,numlist+ct);
cout<<minvalue<<" "<<ct<<endl;
for(int i=0;i<ct;i++){
if(i==ct-1){
printf("%d\n",numlist[i]);
break;
}
printf("%d ",numlist[i]);
}
return 0;
}