来源:POJ2378
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN=10010;
vector<int>tree[MAXN];
bool vis[MAXN];
int dp[MAXN];
int n;
int fa,so;
//题意要求就是我们需要将这个图拆开,使得每一个联通分量都是n/2
//原问题就是这个
//子问题就是我们在一个子图上拆开,是的我的每一个的联通分量小于n/2
//所以我们只要判断其子树满足一个条件即可
//我们贪心的来看这个问题:
//我们删除最少点,所以对于某一棵节点,如果它的有一棵子树的节点大于n/2
//那么这个点显然不是首选点
//因为这个点除此以外的所有子树的和也不过n/2-1(注意其本身)
//此时删去这个点完全没有意义
//从而我们dp计算一个值:一个节点的最多节点子树的度
//我们用dp[rt]来记录rt所有的子树中上述的情况,所以只一次dfs就好
//实际上这个题目和dp关系不大
int dfs(int rt,int fa){
int maxn=0,ans=0;
//if(dp[rt])return dp[rt];
dp[rt]=0;
int sum=1;
for(int i=0;i<tree[rt].size();i++){
if(tree[rt][i]==fa) continue;
maxn=dfs(tree[rt][i],rt);
ans=max(ans,maxn);
sum+=maxn;
}
dp[rt]=max(ans,n-sum);//这个地方需要理解一下,画个图就好
return sum;
}
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=0;i<n;i++) tree[i].clear();
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
for(int i=0;i<n-1;i++){
scanf("%d%d",&fa,&so);
tree[fa].push_back(so);
tree[so].push_back(fa);
}
dfs(1,0);
bool flag=0;
/*for(int i=1;i<=n;i++)
cout<<dp[i]<<" ";
cout<<endl;*/
for(int i=1;i<=n;i++){
if(dp[i]<=n/2){
flag=1;
cout<<i<<endl;
}
}
if(!flag) cout<<"NO"<<endl;
}
return 0;
}