洛谷P2018 訊息傳遞
阿新 • • 發佈:2020-12-08
題目
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; }