1. 程式人生 > 實用技巧 >洛谷P2018 訊息傳遞

洛谷P2018 訊息傳遞

題目

https://www.luogu.com.cn/problem/P2018

思路

由題意得這是一棵樹,而任何一個已經接到訊息的人,都可以把訊息告訴他的一個直接上級或者直接下屬,說明是一棵無根樹。

本來以為要用什麼高階樹上演算法亂搞,結果發現\(N\leq 1000\),這不是dfs就能水過嗎?(實際上是個樹規)

欽定一個結點為根,我們在有根樹上做樹規。對於結點\(x\),他的狀態由他的子結點決定。簡單思考一下可以發現,最優決策一定是先告訴耗時長的子結點。轉移方程不太好寫,直接看程式碼吧。

int dfs(int x){
    int i,num=0,tmp[1010];
    book[x]=1;
    for(i=fst[x];i;i=nxt[i]){
        if(book[to[i]]) continue;
        tmp[++num]=dfs(to[i]);
    }
    sort(tmp+1,tmp+num+1);
    for(i=1;i<=num;i++){
        dp[x]=max(dp[x],tmp[i]+num+1-i);
    }
    return dp[x];
}

這就是最核心的部分。

做了\(n\)次dfs,而每一次的複雜度最壞是\(nlogn\),總時間複雜度\(n^2logn\),完全沒有問題。

程式碼

#include<cstdlib>
#include<queue>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define maxn 1010
using namespace std;
int to[maxn<<1],fst[maxn],nxt[maxn<<1],cnt=0,t[maxn],dp[maxn],book[maxn];
void add(int x,int y){
    to[++cnt]=y;
    nxt[cnt]=fst[x];
    fst[x]=cnt;
}
int dfs(int x){
    int i,num=0,tmp[1010];
    book[x]=1;
    for(i=fst[x];i;i=nxt[i]){
        if(book[to[i]]) continue;
        tmp[++num]=dfs(to[i]);
    }
    sort(tmp+1,tmp+num+1);
    for(i=1;i<=num;i++){
        dp[x]=max(dp[x],tmp[i]+num+1-i);
    }
    return dp[x];
}
int main(){
    int i,j,n,m,x,ans=inf;
    scanf("%d",&n);
    for(i=2;i<=n;i++){
        scanf("%d",&x);
        add(i,x);add(x,i);
    }
    for(i=1;i<=n;i++){
        memset(dp,0,sizeof(dp));
        memset(book,0,sizeof(book));
        t[i]=1+dfs(i);
        ans=min(ans,t[i]);
    }
    printf("%d\n",ans);
    for(i=1;i<=n;i++)
        if(t[i]==ans) printf("%d ",i);
    system("pause");
    return 0;
}