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

BZOJ1217:[HNOI2003]消防局的設立

我對貪心的理解:https://www.cnblogs.com/AKMer/p/9776293.html

題目傳送門:https://www.lydsy.com/JudgeOnline/problem.php?id=1217

顯然對於樹最下面那一層點,肯定都會被父親上或者祖父上的消防局管理。我們就可以從深度最深的點開始貪心。我們把點按深度從大到小排序,對於當前點如果沒有被父親、祖父、兒子、孫子、兄弟上的消防局管理就應該在這個點的祖父處新建一個消防局,這樣顯然可以管理更多點。父親和祖父上有沒有消防局很容易判斷,兒子和孫子上有沒有消防局我們可以在兒子或孫子上建消防局的時候更新父親或祖父的資訊來判斷。那麼兄弟咋整呢?暴力去列舉的話顯然不行。

所以我們開一個數組\(dis\)記錄某些神奇的資訊。\(dis[i]\)表示到\(i\)號點最近的消防局距離是多少。初始都為\(n\)。每次新建一個消防局都去更新這個點的父親和祖父的\(dis\),因為比這個點深度更深的顯然已經不需要更新了。每次判斷兄弟上有沒有消防局只需要判斷父親的\(dis\)是不是等於\(1\)就行了。

時間複雜度:\(O(nlogn)\)

空間複雜度:\(O(n)\)

程式碼如下:

#include <cstdio>
#include <algorithm>
using namespace std;

int n,ans;
int fa[1005],dep[1005],dis[1005],id[1005];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

bool cmp(int a,int b) {
    return dep[a]>dep[b];
}

int main() {
    n=read();
    for(int i=2;i<=n;i++) {
        id[i]=i,fa[i]=read();
        dep[i]=dep[fa[i]]+1,dis[i]=n;
    }id[1]=1;dis[1]=dis[0]=n;
    sort(id+1,id+n+1,cmp);//按深度排序
    for(int i=1;i<=n;i++) {
        int u=id[i],f=fa[u],ff=fa[f];
        dis[u]=min(dis[u],min(dis[f]+1,dis[ff]+2));//用兄弟或者祖父來跟新自己的dis
        if(dis[u]>2) {//如果當前點無人管理,那就在祖父處新建消防局
            dis[ff]=0;ans++;int fff=fa[ff],ffff=fa[fff];
            dis[fff]=min(dis[fff],1);
            dis[ffff]=min(dis[ffff],2);//在祖父的父親和祖父處更新dis資訊
        }
    }
    printf("%d\n",ans);
    return 0;
}