poj 2378 Tree Cutting (树形dp,记忆化搜索)

题目链接: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;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值