樣例輸入
4
5
1
1 2 3
1 3 4
1 4 5
2 3 8
3 4 2ios
樣例輸出
4c++
樣例說明
下圖是樣例說明。算法
這道題看題幹很麻煩,可是想想,要求用時最少的樹結構,也就是求每層次中的最大值,再比較全部層次中的最大值,讓這個總最大值最小。那麼能夠想到,讓這種最大值最小,獲得的確定是這個圖的最小生成樹。那麼這個問題其實就是求這個圖的最小生成樹的最長邊。數組
對最小生成樹,咱們能夠採用prim算法和kruskal算法。數據結構
prim算法相似dijkstra算法,選擇鄰接矩陣實現,須要咱們用n*n維矩陣,對於題中條件,須要用25億個整型字符空間,顯然越界了。我用prim寫的程序,交上去只得了50分。爲了得滿分,仍是須要用kruskal算法。函數
kruskal算法比較容易理解,就是對全部邊升序排列,從最小邊開始,逐次向解中加入邊,每次加入前先判斷,此條邊加入後是否已成環,若是是,則跳過此邊換下一個,若是否,就在答案生成樹中加入此邊。這裏碰到一個問題,如何判斷是否成環?spa
這裏引入並查集數據結構,它可以在幾乎線性的時間複雜度內判斷元素是否屬於同一個集合。則應用到該題中,就是對於新邊的兩端點,若是咱們經過並查集查詢到它們已在同一個集合內,則加入該邊構成環,則不加入;若是沒在同一個集合內,則加入答案生成樹,而且將兩端點加入同一個集合內。code
並查集的實現是靜態樹結構,也就是數組模擬的樹。每個節點的編號同時做爲集合序號,該節點做爲屬於這一集合的全部元素的父節點。blog
並查集的創建通常只包括三個函數:init()、Find()、unite()、same().ci
init:初始化並查集
Find:尋找目標節點的父節點,也就是表明所在的集合
unite:將兩個節點合所在集合併到,合併規則依照所在集合樹的高度
same:判斷兩個節點是否在同一個集合內
下面在答案中進一步說明並查集和kruskal算法。
補充一點,爲了便於處理,我在程序中構造了一個edge類,表示邊。其實class和struct差很少。
#include <bits/stdc++.h> #define N 50005 #define M 100005 using namespace std; int n,m,root,ans=-1; class edge { public: int u,v,cost; edge() : u(0),v(0),cost(0) {} edge(int a,int b,int c) : u(a),v(b),cost(c){} bool operator < (edge const ed) const { return cost<ed.cost; } }; edge es(); vector<edge> ve; int par[N],h[N]; //建立並查集父節點集合par,和並查集樹高集合h。h用於並查集合並。 void init(int n); //並查集初始化函數 int Find(int a); //並查集查找函數 bool same(int a,int b); //並查集判斷是否屬於同一個集合 void unite(int a,int b); //並查集合並函數 int main() { ios::sync_with_stdio(false); cin.tie(0); int a,b,c,res=0; cin>>n>>m>>root; for (int i=0;i<m;i++) { cin>>a>>b>>c; ve.push_back(edge(a,b,c)); } init(n); sort(ve.begin(),ve.end()); for (int i=0;i<(int)ve.size();i++) { if (!same(ve[i].u,ve[i].v)) { //若是該邊的兩定點構不成環,則該邊加入解中 unite(ve[i].u,ve[i].v); int t=ve[i].cost; res+=t; ans=max(t,ans); } } cout<<ans<<endl; return 0; } void init(int n) { for (int i=0;i<=n;i++) { par[i]=i; //初始化,全部的節點的父節點設爲自身 h[i]=0; //全部並查集的高度設爲0,即樹高。 } } int Find(int a) { if (a==par[a]) return a; //若是節點號等於父節點號,這說明找到了該集合的父節點,則返回 return par[a]=Find(par[a]); //路徑壓縮,這一步是並查集算法的精華!!!它可以將向根節點查找的路徑上的全部父節點所有掛靠到根節點上,從而大大縮短並查集接下來查找的執行時間。 } void unite(int a,int b) { a=Find(a); b=Find(b); //原先WA是由於我這裏沒有對a和b的賦值,直接寫了個判斷 if Find(a)==Find(b),找bug找了半天。此處必定注意,要用a、b的根節點來替換a、b。 if (a==b) return; if (h[a]<h[b]) par[a]=b; //若是a集合樹高小於b集合樹高,則a掛靠到b上 else { //若是b集合樹高小於a集合樹高 par[b]=a; //首先將b掛靠到a上 if (h[a]==h[b]) h[a]++; //而後判斷ab樹高是否相同,該操做是否會令a樹變高。如等高,則掛靠會讓a樹高+1 } } bool same(int a,int b) {return Find(a)==Find(b);} //並查集查找,返回對兩元素根節點的判等