treecut-树型DP
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\n",r[i]);
return 0;
}

浙公网安备 33010602011771号