[HNOI2003]消防局的设立

P2279 [HNOI2003]消防局的设立

题目大意:

有一棵树,有一些标记节点可以覆盖距离小于 \(2\) 的点,要求所有点都被覆盖,求这些节点个数最小值。

分析:

进行贪心分析:我们标记一个节点时,如果标记了根节点,那么一定是比叶子节点更加优秀的点。

这个贪心很好证明,画个图就可以了。

我们通过深度进行节点的排序,让节点从深到浅排,这样能够更好找到根节点。

定义 \(dis[i]\) 为当前标记距离点 \(i\) 的最短距离。

我们按照 \(dep\) 枚举出儿子节点的 父亲,爷爷,判断最小距离。

如果\(dis > 2\) ,显然这时候定义爷爷为标记节点最优秀,于是我们就进行标记 \(ans++,dis[grandfather]=0\)

然后对爷爷的父亲和爷爷,进行判断最小距离(因为浅节点无法到达深节点,只能直接判断)。

此时不用管儿子节点,因为肯定不能访问(原因同上面括号)

最后输出标记个数即可!

代码:

#include<bits/stdc++.h>
using namespace std;

const int N=2e3+5;
int n,ans;
int b[N],fa[N],dep[N],dis[N];
bool cmp(int a,int b){return dep[a]>dep[b];}
int main(){
    cin>>n;
    b[1]=1; dis[1]=dis[0]=N;
    for(int i=2,x;i<=n;i++){
        scanf("%d",&fa[i]); dep[i]=dep[fa[i]]+1; b[i]=i; dis[i]=N; 
    } 
    sort(b+1,b+n+1,cmp);//根节点覆盖更广,因此通过深度进行排序。
    for(int i=1;i<=n;i++){
        int son=b[i],father=fa[b[i]],grandfather=fa[fa[b[i]]];
        dis[son]=min(dis[son],min(dis[father]+1,dis[grandfather]+2));
        if(dis[son]>2){
            dis[grandfather]=0, ans++;
            dis[fa[grandfather]]=min(dis[fa[grandfather]],1);
            dis[fa[fa[grandfather]]]=min(dis[fa[fa[grandfather]]],2);
        } 
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}
posted @ 2021-09-22 19:06  Evitagen  阅读(20)  评论(0编辑  收藏  举报