题目
https://www.luogu.com.cn/problem/P2018
思路
由题意得这是一棵树,而任何一个已经接到消息的人,都可以把消息告诉他的一个直接上级或者直接下属,说明是一棵无根树。
本来以为要用什么高端树上算法乱搞,结果发现(Nleq 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
",ans);
for(i=1;i<=n;i++)
if(t[i]==ans) printf("%d ",i);
system("pause");
return 0;
}