treecut-树型DP

1022: treecut

Time Limit: 1 Sec  Memory Limit: 128 MB

Description

  有一个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;
}

 

posted @ 2020-09-02 23:14  岚默笙  阅读(175)  评论(0编辑  收藏  举报