题意:
给定一棵树和一个整数
,可以在树的任意位置放置一个人,这个人可以监管距离其所在节点不超过
的所有节点,问最少放置多少个人可以监管整棵树的所有节点。
思路:
此题的解题思路还挺具有启发意义。
对于树的相关问题,我们可以考虑先转换成线性问题来做,如果是线性的话,很显然最优解应该是从某一端的边界开始,每个长度为 的区间的中心均放置一个人,若最后的距离不足 ,则也在其中心放置一个人。
我们可以推广贪心的思想到树形结构上:
对于这棵树,我们可以从叶子节点出发,定义
:
若
:则代表以
为根节点的子树可以向上监管距离不超过
的节点
若
:则代表以
为根节点的子树需要其他节点帮助其监管距离为
的节点
对于当前考虑的节点
,我们可以求出其子节点
值的最小值
和最大值
,随后分以下情况讨论:
若
为叶子节点,则说明需要其他节点帮助提供监管,更新
。
若
说明存在某一个以
的子节点为根的子树,需要其他节点帮助提供
个单位的监管距离,故由
提供,更新
。
若
则说明有一个子树放置的人能够把其他全部需要监管的村庄都覆盖,则更新
若以上情况均不满足,则说明可以继续向上查找更优的放置点,更新
故此题得解。
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int INF = 1e8 + 10;
const int A = 2e5 + 10;
class Gra{
public:
int v,next;
}G[A<<2];
int head[A],dp[A],n,k,tot,ans;
void add(int u,int v){
G[tot].v = v;
G[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u,int pre){
int Mn = INF,Mx = -INF;
for(int i=head[u] ;i!=-1 ;i=G[i].next){
int v = G[i].v;
if(v == pre) continue;
dfs(v,u);
Mn = min(Mn,dp[v]);
Mx = max(Mx,dp[v]);
}
if(Mn == INF) dp[u] = -1;
else if(Mn <= -k){
dp[u] = k;
ans++;
}
else if(Mn + Mx > 0) dp[u] = Mx - 1;
else dp[u] = Mn - 1;
}
int main(){
memset(head,-1,sizeof(head));
tot = 0;
scanf("%d%d",&n,&k);
if(k == 0) printf("%d\n",n);
else{
for(int i=1 ;i<n ;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
memset(dp,0,sizeof(dp));
ans = 0;
dfs(1,1);
if(dp[1] < 0) ans++;
printf("%d\n",ans);
}
return 0;
}