题目链接:http://poj.org/problem?id=2378
参考链接:http://www.cnblogs.com/seaupnice/p/9471700.html
题意:给你n个节点,然后n-1条边,让你从中移除一个点,使得每一个联通快中的节点数不大于n/2。
题解:其实这就是个记忆化搜索,
访问每个node,维护两个值:
1,所有子树的结点数的最大值childmax,
2,所有子树(这里包括node)的结点数之和sum。
递归过程中用上一层的sum,不断更新这一层的childmax。
而childmax和sum则共同用来判断这个node是否可以删除。
下面再分析判断条件: childmax<=n/2 && n-sum<=n/2
childmax<=n/2 :去掉node后,原先node的子树均满足条件。
n-sum<=n/2 :去掉node后,原先除node和node的所有子树外的树(就当是node的祖先树吧)均满足条件。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=100010;
vector<int> G[maxn];
int ans[maxn],tot,n;
int dfs(int root,int pre)
{
int sum=1,childmax=0;///若是叶子结点则return sum=1,否则求其子树(包括自己)的总结点数
int len=G[root].size();
for(int i=0;i<len;i++)
{
if(G[root][i]==pre) continue; ///无向图中防止成环
int son=dfs(G[root][i],root);
sum+=son; ///计算子树的节点之和
childmax=max(childmax,son);///所有子树的节点最大值
}
childmax=max(childmax,n-sum);
if(childmax<=n/2)
{
ans[++tot]=root;///说明此节点满足条件,存储起来
}
return sum; ///递归上一层
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
G[i].clear();
}
int x,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y); ///建立双向边
G[x].push_back(y);
G[y].push_back(x);
}
tot=0;
dfs(1,0);
sort(ans+1,ans+1+tot);///题目要从小到大输出
if(tot){
for(int i=1;i<=tot;i++)
printf("%d\n",ans[i]);
}
else puts("NONE");
}
return 0;
}