题目大意
给你一棵树 根节点为1
规定一种节点为树芽:
1树芽不能是根节点
2树芽不能是叶子节点
3树芽的所有子节点都是叶子节点
树芽可以任意移动 即:树芽可以切断自己与父节点的联系 然后带着自己的子孙们 链接任意节点
可以移动无数次
问你如何移动会让最后树的所有叶子节点最少
题目思路一
我们要推导出一个结论
一个芽节点的移动会导致叶子节点减少 当且仅当 该芽的父亲节点有其他的孩子
如下图中节点2即为可减节点
如下图中4为不可减节点 但是当该节点消去之后 2节点就变为了可减节点
于是我们进行树上搜索
ans=所有的叶子节点
对于每一个节点 分为三类
1第一类 是叶子节点
2第二类 是芽节点
3第三类 啥也不是
那么对于每个节点 搜索当前节点的子节点的各种节点数量
int tot=0;//总量
int yz=0;//叶子
int kq=0;//芽节点(可切)
int qt=0;//其他
如果没有任何子节点则 为叶子节点 返回 1
若 所有节点都是叶子节点 则该节点为芽返回 2
都不是返回3
该节点的子节点存在可切子节点(即子节点为芽节点)
则需要将该节点进行处理
若所有子节点都可切 那么ans-=kq-1
为什么要减1 因为之前推出的结论 一个芽节点的移动会导致叶子节点减少 当且仅当 该芽的父亲节点有其他的孩子
减到最后父节点没有孩子节点了 当然无法减1了
若只有部分节点可切 那么ans-=kq 为什么不减1 因为无论如何减该芽的父节点都有孩子节点
剪完之后 在判断目前的点是什么点
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int head[maxn],cnt,T,n;
int x,y,ans=0;
struct node{
int to,next;
int dis;
}e[maxn*2];
void add(int x,int y,int z)
{
e[++cnt].dis=z;
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt;
}
int dfs(int now,int fa)
{
int u=now;
int tot=0;//总量
int yz=0;//叶子
int kq=0;//芽节点(可切)
int qt=0;//其他
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa)continue;
tot++;
int val=dfs(v,u);
if(val==1)yz++;
else if(val==2)kq++;
else qt++;
}
//if(now==1)return 1;
if(tot==0)return 1;//如果没有任何子节点则 为叶子节点 返回 1
if(tot==yz)return 2;//若 所有节点都是叶子节点 则该节点为芽返回 2
if(kq!=0)
{
if(kq==tot)
{
//若所有子节点都可切 那么ans-=kq-1
ans-=tot-1;
return 1;
}
//若只有部分节点可切 那么ans-=kq 为什么不减1 因为无论如何减该芽的父节点都有孩子节点
ans-=kq;
if(qt==0)
{
return 2;
}
else
{
return 3;
}
}
return 3;
}
void init()
{
cnt=0;
ans=0;
for(int i=0;i<=n;i++)
{
head[i]=0;
}
}
int main()
{
cin>>T;
while(T--)
{
cin>>n;
init();
for(int i=1;i<n;i++)
{
cin>>x>>y;
add(x,y,1);
add(y,x,1);
}
for(int i=1;i<=n;i++)//计算ans
{
int num=0;
for(int j=head[i];j;j=e[j].next)
{
int v=e[j].to;
if(v==i)continue;
num++;
}
if(num==1&&i!=1)ans++;
}
//cout<<ans<<endl;
dfs(1,0);
cout<<ans<<endl;
}
return 0;
}
/*
1
6
1 2
1 3
2 4
2 5
3 6
*/
题目思路二
队友写的树上dp
代码很详细
代码
#include<stdio.h>
#include<iostream>
#include<sstream>
#include<string>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cctype>
#include<stack>
#include<set>
#include<map>
#include<unordered_map>
using namespace std;
typedef long long int ll;
const double pi=acos(-1.0);
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const int N=2e5;
struct Edge{
int from,to,np;
};
int dp[N+3];
Edge edge[2*N+3];
int tot,n;
int head[N+3];
void init();
void add(int x,int y);
int get_dp(int x,int father);
int main(){
//ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--){
init();
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
get_dp(1,-1);
printf("%d\n",dp[1]);
}
return 0;
}
void init(){
tot=0;
memset(dp,0,sizeof dp);
memset(edge,0,sizeof dp);
memset(head,-1,sizeof head);
}
void add(int x,int y){
tot++;
edge[tot].from=x;
edge[tot].to=y;
edge[tot].np=head[x];
head[x]=tot;
}
int get_dp(int x,int father){//返回这个x节点是否可以切割,可以就返回1,否则为0
//printf("x=%d\n",x)
int cnt=0;//记录不可切割的子节点的数量
for(int i=head[x];i!=-1;i=edge[i].np){
int y=edge[i].to;
if(y==father) continue;
if(!get_dp(y,x)){
dp[x]+=dp[y];//dp[x]表示以x为根节点的最少分割的叶子节点数量
cnt++;
}else{
dp[x]+=dp[y]-1;
}
}
if(cnt==0) dp[x]++;
//printf("dp[%d]=%d\n",x,dp[x]);
if(cnt==0) return 0;
else return 1;
}