洛谷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;
}
posted @ 2020-12-08 14:45  文艺平衡树  阅读(76)  评论(0编辑  收藏  举报