Codeforces Global Round 16 E. Buds Re-hanging

题意

给定一颗n个节点,n-1条边,以1号节点为根节点的树。引入一个bud节点,bud节点不是根节点,也不是叶子节点,但是他的子节点都是叶子节点。你可以进行无限次以下操作:将bud节点和他的所有叶子节点移到任意节点的下方,也就是将bud节点从原来的地方断开变成别的节点的子节点。如下图图1到图2的过程。
请添加图片描述
图1中2号节点为bud节点,将其与1号根节点连接的边断开,链接到3号节点下方得到图2.
要求对图进行操作后得到最少的叶子节点数量是多少。

思路

先将所有bud节点操作后都变为与根节点直接相连,那么图中就只剩下根节点,叶子节点,还有bud节点,要使叶子节点最少,就是将所有bud节点移到某一个叶子节点下方。简单证明一下正确性:bud节点原来连接着其他一个点,从原来的地方断开,他的父亲节点可能不会变为叶子节点,但是把他从这里断开,放到别的叶子节点下面,一定会少了一个叶子节点,就是按照这个思路来的,将所有的bud都从原来的地方断开,找到任意一个叶子节点覆盖到他的下方,只是我这里多了一个操作,先把他连接到1号节点上,然后在覆盖到别的叶子节点下方。
请添加图片描述
如上图,将所有的bud节点连接到1号根节点后如下图。请添加图片描述
将所有的bud节点连接到1号节点上其实就相当于求最多叶子节点的过程。这个过程用dfs,然后用一个d数组记录,d[x]表示x节点下方叶子节点有多少个。具体深搜思路是碰到叶子节点返回1,叶子节点上方那个节点d[x]+1,x也就是叶子节点上方那个节点返回0,如果一个节点的子节点都返回0,说明下方那个节点是bud节点,需要移走,那么他就是叶子节点,这个节点返回1,就是这么个深搜思路,下面上代码喽O(∩_∩)O。

代码示例

#include<bits/stdc++.h>
using namespace std;
const int maxn=4*1e5+10;
int head[maxn],v[maxn],Next[maxn],w[maxn],vis[maxn],d[maxn];
//d[x]表示将所有bud节点连接到根节点下方后他下方叶子节点数目
int T;
int n;
int tot=0;
void add(int u,int y)
{
    v[++tot]=y,Next[tot]=head[u],head[u]=tot;
}
int dfs(int x)//dfs将所有bud节点连接到根节点上
{
    vis[x]=1;
    int f=1;
    int ans=1;//先当这个节点下方所有节点都是bud节点
    for(int i=head[x];i;i=Next[i])
    {
        int y=v[i];
        if(vis[y])
        continue;
        f=0;
        //判断该点下方是否有叶子节点,有的话需要返回0,令ans=0
        if(dfs(y)==1)
        d[x]++,ans=0;
    }
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);//输入节点数
        for(int i=1;i<=n;i++)//初始化
        vis[i]=0,head[i]=0,d[i]=0;
        tot=0;
        for(int i=1;i<n;i++)//添加边
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs(1);//将所有bud节点连接到根节点上
        int p=1;//将所有的bud节点放到p节点下方某个叶子节点上
        int ans=0;
        for(int i=1;i<=n;i++)//找到一个下方有叶子节点的点
        {
            if(d[i])
            {
                ans=d[i];
                p=i;
                break;
            }
        }
        
        for(int i=1;i<=n;i++)
        {
            if(i!=p&&d[i])
            {
                ans+=d[i]-1;//d[i]-1是因为他覆盖了一个叶子节点
            }
        }
        printf("%d\n",ans);
    }

return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值