1. 程式人生 > 其它 >[HNOI2003]消防局的設立

[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;
}