题意
给定一颗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;
}