2017.07.09【NOIP提高组】模拟赛B组 treecut 题解

原题:

https://jzoj.net/senior/#contest/show/2043/2

题目描述:

有一个N个节点的无根树,各节点编号为1..N,现在要求你删除其中的一个点,使分割开的连通块中节点个数都不超过原来的一半多。

输入:

第一行:一个整数N (1 <= N <= 10,000)。   后面有N-1行:每行两个整数 X 和 Y,表示一个边连接的两个节点号。

输出:

输出所有可能选择的点。如果有多个节点,按编号从小到大输出,每个一行。 如果找不到这样的点,输出一行:"NONE".

样例输入:

10 1 2 2 3 3 4 4 5 6 7 7 8 8 9 9 10 3 8

样例输出:

3 8

样例说明:

删除3号或8号节点,则分枝最多有5个节点

分析:

既然是树,我们为了方便就把它弄成有根树。
这道题跟节点数有关,我们就可以用一个DFS来求出当前节点的儿子树


2017.07.09【NOIP提高组】模拟赛B组 treecut 题解_第1张图片
6631287668030149991.jpg

红色数字表示这个节点作为父亲节点时的树的大小
题目要求,我们要找一个点,删除后要求剩下的联通块节点数不超过总数的一半。
比如,删除3


2017.07.09【NOIP提高组】模拟赛B组 treecut 题解_第2张图片
1625799465581247064.jpg

黄色这一块就是父亲节点减去自己,就是10-8=2
红色和蓝色就是3的子树大小。
我们可以通过一个DFS,每步回溯子树大小,判断是否能否删除此节点

实现:

#include
#include
#include
#include
using namespace std;

int DFS(int);
int DFS_ANS(int);

vector  ljlb[10007];
bool used[10007],ans[10007];
int childn[10007];
int i,u,v,n,j,mid;

int main()
{
    for (i=0;i<10007;i++) childn[i]=1;
    cin>>n;
    mid=n>>1;
    for (i=0;i>u>>v;
        ljlb[u].push_back(v);
        ljlb[v].push_back(u);
    }
    used[1]=true; 
    childn[1]=DFS(1);
    memset(used,false,sizeof(used));
    used[1]=true;
    childn[1]=DFS_ANS(1);
    for (i=1;i<=n;i++)
        if (!ans[i]) cout<mid) ans[root]=true;
    for (int j=0;jmid) ans[root]=true;
       }
    }
    return childn[root];
}

你可能感兴趣的:(2017.07.09【NOIP提高组】模拟赛B组 treecut 题解)