題意
給你一個森林(若干棵樹),向其中加邊,使得最后形成一棵樹,要求最后形成的樹的直徑最小,輸出這棵樹的直徑和所加的邊
分析
每加一條邊,都可以將兩棵樹合並成一棵樹,總共有t = n - m棵樹
也就是說我們需要在這t棵樹里面,分別選一個點,將他們連接起來,使得最后形成一棵直徑最小的樹
這個點的性質是,以這個點為根節點轉化為有根樹的畫,樹的高度一定是最小的(與重心類似,最長鏈最短可以保證合並后直徑最短)
稍加分析可以知道,這個點一定是直徑的中點(直徑兩端到該點的距離相差不超過1)
所以我們維護一棵樹,可以維護以下幾點性質:
1. 樹的直徑dig
2. 樹的直徑的中點序號center
3. 樹的直徑兩端離center較遠的點(相同則任意)site
這樣合並A,B兩顆樹后,新的樹
dig = max ( A.dig , B.dig , ceil(A.dig/2) + ceil(B.dig/2) )
遍歷這棵樹,仍然求得新的center和site
那么以怎樣的順序合並呢?
通過上面dig的轉移式可以看出,如果兩棵樹site到center的距離不相等,則形成的新樹的直徑不變(等於大的一棵樹),否則直徑變大
所以我們希望最小化這個影響(不變大)
考慮最大的三棵樹$A.dig ≥ B.dig ≥ C.dig$
第一次將最大的兩顆樹合並起來,得到的一棵新樹D
如果$A.dig == B.dig$
則$D.dig > C.dig$則之后D和C合並直徑不會變
否則$A.dig > C.dig$之后D和C合並直徑不會變
可證得每次合並最大的兩棵樹
如此操作下去直到合成一棵樹,得到的直徑一定最小
其實不用優先隊列維護,只需要排序后,每次取未合並的最大的樹,與當前維護的樹合並即可(類似prim算法合並的思路)
代碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int n,m; 5 vector<int>edge[1005]; 6 int color[1005]; 7 void rs(int pre,int now,int co) { 8 color[now] = co; 9 for(auto it = edge[now].begin();it!=edge[now].end();it++) { 10 if(*it == pre) continue; 11 rs(now,*it,co); 12 } 13 return; 14 } 15 struct Tree{ 16 int dig,center,site; 17 bool operator < (const Tree& o) const { 18 return dig < o.dig; 19 } 20 }tree; 21 priority_queue<Tree>pq; 22 int far,X,Y,id; 23 void dfs(int pre,int now,int dis){ 24 if(dis>far) { 25 far = dis;id =now; 26 } 27 for(auto it = edge[now].begin();it!=edge[now].end();it++) { 28 if(*it == pre) continue; 29 dfs(now,*it,dis+1); 30 } 31 } 32 stack<int>st; 33 int flag; 34 vector<pair<int,int> >ans; 35 void dfs2(int pre,int now,int aim){ 36 if(now == aim) { 37 tree.site = now; 38 for(int i=1;i<(far+1)/2;i++){ st.pop();} 39 tree.center = st.top(); 40 tree.dig = far; 41 pq.push(tree); 42 flag = 1; 43 while(!st.empty())st.pop(); 44 return; 45 } 46 st.push(now); 47 for(auto it = edge[now].begin();it!=edge[now].end();it++) { 48 if(*it == pre) continue; 49 dfs2(now,*it,aim); 50 if(flag) return; 51 } 52 st.pop(); 53 } 54 int main() { 55 ios::sync_with_stdio(false); 56 cin>>n>>m; 57 int x,y; 58 for(int i=1;i<=m;i++) { 59 cin>>x>>y; 60 edge[x].push_back(y); 61 edge[y].push_back(x); 62 } 63 int co = 0; 64 for(int i=1;i<=n;i++) { 65 if(color[i]==0) { 66 rs(0,i,++co); 67 far=0;id = i; 68 dfs(0,i,0); 69 X = id; 70 far=0;id = X; 71 dfs(0,X,0); 72 Y = id; 73 if(X == Y) { 74 tree.site = X; 75 tree.center = X; 76 tree.dig = 0; 77 pq.push(tree); 78 } 79 else { 80 flag =0; 81 dfs2(0,X,Y); 82 } 83 } 84 } 85 while(pq.size()>1) { 86 Tree a = pq.top();pq.pop(); 87 Tree b = pq.top();pq.pop(); 88 far = max(a.dig,((a.dig+1)/2) + ((b.dig+1)/2) +1); 89 far = max(b.dig,far); 90 edge[a.center].push_back(b.center); 91 edge[b.center].push_back(a.center); 92 flag = 0; 93 dfs2(0,a.site,b.site); 94 ans.push_back(make_pair(a.center,b.center)); 95 } 96 tree = pq.top(); 97 cout<<tree.dig<<endl; 98 99 for(auto it = ans.begin();it!=ans.end();it++) { 100 cout<<it->first<<" "<<it->second<<endl; 101 } 102 103 104 }