[HNOI2003]消防局的設立
阿新 • • 發佈:2021-09-22
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; }