题目描述
给定一棵树,要求用若干个点将整棵树完全覆盖,其中每个点都可以覆盖与它的距离不超过 k k k的节点. 求最小的这样的点的数量.
题目分析
如果 k = 1 k=1 k=1或 k = 2 k=2 k=2,那么显然可以使用树形dp来做;但是当这个 k k k很大的时候,动态规划就不适用了.
我们不妨回到题目:到底要求什么?
我们要求的是最小的覆盖整棵树的点数.
每个点都需要被覆盖.
贪心:
这道题可以使用树形dp,这提示我们,无论把哪个点作为根,最后的结果都是一样的.
所以我们任意指定一个点为根节点,找出这棵树深度最大那个点,这个点一定需要被覆盖.
而且如果我们需要覆盖它,当然是要让覆盖它的点越浅越好,因为这个覆盖它的点越浅,能覆盖的点就越多.
那我们就找到当前最深的点,把覆盖它的点找到(和最深的点有k的距离),把这个覆盖它的点的所有能覆盖到的点打上标记,重复这个过程,直到整棵树全都被覆盖为止.
显然这样算出来的答案一定是最小的,不可能找到一种更小的方案. 时间复杂度为 O ( n k ) O(nk) O(nk), k k k为一个点能覆盖的范围.
程序实现
//P3942 将军令
#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
struct edge{
int v,next;
}e[maxn<<1];
int head[maxn],tot;
void add(int u,int v){
e[++tot].v =v;
e[tot].next =head[u];
head[u]=tot;
}
struct node{
int dep,fa,pos;
bool operator <(node x)const {
return x.dep <dep;
}
}s[maxn];
int n,len,type,ans;
vector<int >g[maxn<<1];
void init(int u,int pre){
s[u].dep =s[pre].dep +1;
g[s[u].dep ].push_back(u);
for(int i=head[u];i;i=e[i].next ){
int v=e[i].v ;
if(v==pre)continue;
init(v,u);
}
for(int i=g[s[u].dep +len].size()-1;i>=0;i--){
int v=g[s[u].dep +len][i];
s[v].fa =u;
g[s[u].dep +len].pop_back();//只是对当前u的子树进行操作,不是所有的这个深度的点都在vector里,所以要pop
}//找到每个点的覆盖它的点,vector维护每个深度的点有多少,以方便查找覆盖它的点
}
bool vis[maxn];
void dfs(int u,int dis,int pre){
if(dis>len)return ;
vis[u]=true;
for(int i=head[u];i;i=e[i].next ){
int v=e[i].v ;
if(v==pre)continue;
dfs(v,dis+1,u);
}
}//对于每个没有覆盖到的深度最大的点,找到覆盖它的点并进行标记操作
int main(){
scanf("%d%d%d",&n,&len,&type);
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for(int i=1;i<=n;i++) s[i].pos =i;
init(1,1);//第一次dfs进行预处理,预处理每个点的深度和每个点的覆盖它的点
//注意fa[1]=1,这样能够保证k>max_dep时的正确性.
sort(s+1,s+n+1);//按深度排序
for(int i=1;i<=n;i++){
if(vis[s[i].pos ])continue;//如果这个点最深而且未访问
dfs(s[i].fa ,0,s[i].fa );
//第二次dfs找到覆盖它的点,并把这个点所能覆盖到的点全部打上标记
ans++;
}
printf("%d\n",ans);
return 0;
}