題意:ios
有一個n個點m條邊的圖,每條邊有個權值,如今要求此圖全部生成樹中最大權值和最小權值的差(暫且稱爲極差)的最小值。算法
分析:優化
若是暴力全部生成樹複雜度是O(m^2)必然T,因此須要優化。spa
參考Kruskal算法,把邊按權值從小到大排序,一條邊一條邊的加入,若是發現有環,說明這個環,能夠進行優化,使接下來的生成樹極差更小(加入的更大),這步優化就是刪除這個環裏最小的邊。code
這樣,只須要在加入新的邊以前,判斷圖中新邊兩點是否已經連通,而且返回權值最小的邊。blog
而後刪除最小邊,加入新的邊。排序
一旦當前圖中邊數==總頂點個數-1,就說明這是一顆生成樹,更新一波最小值。ci
這個複雜度是O(n*m),官方題解說用並查集增刪邊能夠使複雜度爲O(mlogn)。string
其實我一開始想的就是用並查集,用find能夠飛速判斷是否成環。it
若是不進行路徑壓縮,貌似跟普通dfs判連通沒啥區別了。
若是進行了路徑壓縮,若是刪除的是與根節點直接相連的邊,感受就BOOM了。
因此,不會~
代碼:
1 #include <set> 2 #include <map> 3 #include <list> 4 #include <cmath> 5 #include <queue> 6 #include <stack> 7 #include <vector> 8 #include <bitset> 9 #include <string> 10 #include <cctype> 11 #include <cstdio> 12 #include <cstring> 13 #include <cstdlib> 14 #include <iostream> 15 #include <algorithm> 16 // #include <unordered_map> 17 18 using namespace std; 19 20 typedef long long ll; 21 typedef unsigned long long ull; 22 typedef pair<int, int> pii; 23 typedef pair<ull, ull> puu; 24 25 #define inf (0x3f3f3f3f) 26 #define lnf (0x3f3f3f3f3f3f3f3f) 27 #define eps (1e-9) 28 #define fi first 29 #define se second 30 31 bool sgn(double a, string select, double b) { 32 if(select == "==")return fabs(a - b) < eps; 33 if(select == "!=")return fabs(a - b) > eps; 34 if(select == "<")return a - b < -eps; 35 if(select == "<=")return a - b < eps; 36 if(select == ">")return a - b > eps; 37 if(select == ">=")return a - b > -eps; 38 } 39 40 41 //-------------------------- 42 43 const ll mod = 1000000007; 44 const int maxn = 10010; 45 46 struct Edge { 47 int u, v; 48 int val; 49 50 bool operator<(const Edge &a)const { 51 if(val != a.val)return val < a.val; 52 else if(u != a.u)return u < a.u; 53 else return v < a.v; 54 } 55 56 bool operator==(const Edge &a)const { 57 if(u == a.u && v == a.v && val == a.val)return true; 58 if(u == a.v && v == a.u && val == a.val)return true; 59 return false; 60 } 61 62 63 } edge[150010]; 64 65 66 set<int> G[400]; 67 bool vis[400]; 68 int vs[400][400]; 69 70 set<Edge> subset; 71 Edge lightest; 72 73 74 bool circy(int u, int v) { 75 if(u == v)return true; 76 vis[u] = true; 77 bool res = false; 78 for(set<int>::iterator it = G[u].begin(); it != G[u].end(); it++) { 79 if(!vis[*it] && circy(*it, v)) { 80 if(lightest.val > vs[u][*it]) { 81 lightest.u = u; 82 lightest.v = *it; 83 lightest.val = vs[u][*it]; 84 } 85 res = true; 86 break; 87 } 88 } 89 return res; 90 } 91 92 void solve() { 93 int n, m; 94 while(scanf("%d%d", &n, &m) && n) { 95 for(int i = 0; i < n; i++) { 96 G[i].clear(); 97 } 98 subset.clear(); 99 for(int i = 0; i < m; i++) { 100 scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].val); 101 if(edge[i].u > edge[i].v)swap(edge[i].u, edge[i].v); 102 vs[edge[i].u][edge[i].v] = vs[edge[i].v][edge[i].u] = edge[i].val; 103 } 104 sort(edge, edge + m); 105 int ans = inf; 106 for(int i = 0; i < m; i++) { 107 lightest = edge[i]; 108 memset(vis, 0, sizeof(vis)); 109 subset.insert(edge[i]); 110 if(circy(edge[i].u, edge[i].v)) { 111 if(lightest.u > lightest.v)swap(lightest.u, lightest.v); 112 G[lightest.u].erase(lightest.v); 113 G[lightest.v].erase(lightest.u); 114 subset.erase(lightest); 115 } 116 G[edge[i].u].insert(edge[i].v); 117 G[edge[i].v].insert(edge[i].u); 118 if(subset.size() == n - 1) { 119 ans = min(ans, (subset.rbegin()->val - subset.begin()->val)); 120 } 121 } 122 printf("%d\n", ans ); 123 } 124 125 126 } 127 128 int main() { 129 130 #ifndef ONLINE_JUDGE 131 freopen("1.in", "r", stdin); 132 freopen("1.out", "w", stdout); 133 #endif 134 // iostream::sync_with_stdio(false); 135 solve(); 136 return 0; 137 }
後記:
在用set的時候出現一堆坑,也說明本身對STL容器知識的匱乏。
set的去重是直接與operator<相關的。
若是你的operator<只寫了比較val,那麼當val相等,可是u和v不等的時候,這條新的邊依然會被過濾掉。
因此要讓u和v也參與一下比較。