Description
给一棵nn个节点的树,要求删去最多的边,使得剩下的边可以满足,从个点中选出kk个点,个点中的每一个点都可以通过剩下的边至少与这kk个点中的另一个点相连,问剩下的最少边数
Input
第一行一整数表示用例组数,每组用例首先输入两个整数nn和,之后输入n−1n−1个正整数a1,...,an−1a1,...,an−1,aiai和i+1i+1相连表示一条树边(1≤T≤100,2≤k≤n≤100000)(1≤T≤100,2≤k≤n≤100000)
Output
对于每组用例,输出剩下的最少边数
Sample Input
2
4 4
1 2 3
4 3
1 1 1
Sample Output
2
2
Solution
贪心,最优是一条边连两个且边互不相交,从树叶开始dfsdfs,找出树的最大匹配数numnum,即这棵树最多可以找到numnum条边不相交,注意到这numnum条最多我们选⌊k2⌋⌊k2⌋条,故取ans=min(num,⌊k2⌋)ans=min(num,⌊k2⌋)表示至多有ansans条边可以互不相交且每条边连kk个点中的两个,这样剩下的个点就只能每个点连一条边到这ansans条边上,答案为k−ansk−ans
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
namespace fastIO
{
#define BUF_SIZE 100000
//fread -> read
bool IOerror=0;
inline char nc()
{
static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
if(p1==pend)
{
p1=buf;
pend=buf+fread(buf,1,BUF_SIZE,stdin);
if(pend==p1)
{
IOerror=1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch)
{
return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
}
inline void read(int &x)
{
char ch;
while(blank(ch=nc()));
if(IOerror)return;
for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
}
#undef BUF_SIZE
};
using namespace fastIO;
#define maxn 100001
struct node
{
int v,next;
}edge[2*maxn];
int tot,head[maxn];
int T,n,k,num;
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v)
{
edge[tot].v=v,edge[tot].next=head[u],head[u]=tot++;
}
int dfs(int u,int fa)
{
int cnt=0;
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].v;
if(v==fa)continue;
cnt+=dfs(v,u);
}
if(cnt)
{
num++;
return 0;
}
return 1;
}
int main()
{
read(T);//scanf("%d",&T);
while(T--)
{
read(n);read(k);//scanf("%d%d",&n,&k);
init();
for(int i=2;i<=n;i++)
{
int a;
read(a);//scanf("%d",&a);
add(a,i),add(i,a);
}
num=0;
dfs(1,1);
num=min(num,k/2);
printf("%d\n",k-num);
}
return 0;
}