原題:https://www.luogu.org/problem/P2279ios
題解轉載自:https://www.luogu.org/blog/contributation/solution-p2279數組
找最低沒被覆蓋到的點,並在它的祖父處設一個消防站。考慮到這個點的全部子孫後代都已經被覆蓋了,所以這時覆蓋祖父能蓋到更多額外的點,並保證結果不會更差。spa
不少思路是用dfs或堆求取最低節點,實際上不必,只要預處理出深度(邊輸入邊處理)並排序,碰到已覆蓋就跳過,未覆蓋就在祖父處設消防站,ans++。blog
問題在於怎樣才能判斷這個點覆蓋到了沒有。對於兒子或孫子覆蓋他,能夠在在兒子處設站時就標記它;而對於父親和祖父覆蓋他,能夠用兒子對父親的映射f來解決;問題在於兄弟。其實,能夠用o數組維護「離i最近的消防站到i的距離」,當o[父親]==1時,就能肯定它是否被覆蓋。排序
代碼it
#include<iostream> #include<cstdio> #include<algorithm> #define N 2020 #define FOR(i,a,b) for(int i=a;i<=b;i++) using namespace std; int n,b[N],f[N],d[N],o[N],ans,u,v,w; bool cmp(int x,int y){return d[x]>d[y];} int main(){ scanf("%d",&n);b[1]=1,o[1]=o[0]=N; FOR(i,2,n) scanf("%d",&f[i]),d[i]=d[f[i]]+1,b[i]=i,o[i]=N; sort(b+1,b+n+1,cmp); FOR(i,1,n){ v=b[i],w=f[v],u=f[f[v]]; o[v]=min(o[v],min(o[w]+1,o[u]+2)); if(o[v]>2){ o[u]=0,ans++; o[f[u]]=min(o[f[u]],1),o[f[f[u]]]=min(o[f[f[u]]],2); } }printf("%d",ans); }
這種方法的普適性很強,能夠解決半徑爲k的最小覆蓋問題。並且不用存圖。只須要把維護「父親和爺爺」改爲維護「上位k位祖先」便可,複雜度O(N*K),常數也很小。io