1022: treecut
Time Limit: 1 Sec Memory Limit: 128 MBDescription
有一个N个节点的无根树,各节点编号为1..N,现在要求你删除其中的一个点,使分割开的连通块中节点个数都不超过原来的一半多。
Input
第一行:一个整数N (1 <= N <= 10,000)。 后面有N-1行:每行两个整数 X 和 Y,表示一个边连接的两个节点号。
Output
输出所有可能选择的点。如果有多个节点,按编号从小到大输出,每个一行。 如果找不到这样的点,输出一行:"NONE".
Sample Input
10 1 2 2 3 3 4 4 5 6 7 7 8 8 9 9 10 3 8
Sample Output
3 8
HINT
样例说明: 删除3号或8号节点,则分枝最多有5个节点
分析:本题要求树的重心(在树分治中会用到),可以通过树型DP维护出子树的大小,再判断该点是否符合条件
即
s[x]++; s[x]=∑s[son]; if(s[x]<half&&s[son]<half)//此处简写 需要比较每个s[son]与half的大小关系 r[++cnt]=x;
最后将r数组排序输出即可
代码如下:
#include<cstdio> #define N 10001 #include<algorithm> using namespace std; int half; int head[N],nxt[2*N],to[2*N]; int s[N]; int r[N]; int n; int tot=0; int cnt=0; void dfs(int x,int f) { int flag=0; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==f) continue; dfs(y,x); if(s[y]>half) flag=1; s[x]+=s[y]; } if(flag==0) { int el=n-1-s[x]; if(el<half) r[++cnt]=x; } } void add(int x,int y) { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } int main() { scanf("%d",&n); half = n/2; for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); s[i]=1; } s[n]=1; dfs(1,0); sort(r+1,r+1+cnt); for(int i=1;i<=cnt;i++) printf("%d ",r[i]); return 0; }