题面
题意
给出一棵树,要求将其叶子分成几个集合,要求每个集合中叶子两两之间的距离不大于k,求至少要将其分成几个集合。
做法
因为n很大,所以考虑贪心。
首先dfs,我们可以自下而上逐个子树考虑,对于一个子树,可以将其拆成几条链,可以将较短的几条链合并在一起,而长的链独自一个集合,然后其父节点与短链集合相连,这样可以保证答案较优。
具体见代码,比较难描述。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define N 1001000
using namespace std;
int n,m,first[N],bb,ds[N],ans;
struct Bn
{
int to,next;
}bn[N<<1];
inline void add(int u,int v)
{
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
first[u]=bb;
}
int dfs(int now,int last)
{
if(ds[now]==1) return 0;
int i,j,p,q,t;
vector<int>use;
use.clear();
for(p=first[now];p!=-1;p=bn[p].next)
{
if(bn[p].to==last) continue;
use.push_back(dfs(bn[p].to,now)+1);
}
sort(use.begin(),use.end());
for(i=use.size()-1;i>=0;i--)
{
t=use[i];
if(i) t+=use[i-1];
if(t<=m) return use[i];
ans++;
}
return -N;
}
int main()
{
memset(first,-1,sizeof(first));
int i,j,p,q;
cin>>n>>m;
for(i=1;i<n;i++)
{
scanf("%d%d",&p,&q);
add(p,q),add(q,p);
ds[p]++,ds[q]++;
}
for(i=1;i<=n;i++) if(ds[i]>1) break;
if(dfs(i,-1)>0) ans++;
cout<<ans;
}