题意:
有一个n个点m条边的图,每条边有个权值,现在要求此图所有生成树中最大权值和最小权值的差(暂且称为极差)的最小值。
分析:
如果暴力所有生成树复杂度是O(m^2)必然T,所以需要优化。
参考Kruskal算法,把边按权值从小到大排序,一条边一条边的加入,如果发现有环,说明这个环,可以进行优化,使接下来的生成树极差更小(加入的更大),这步优化就是删除这个环里最小的边。
这样,只需要在加入新的边之前,判断图中新边两点是否已经连通,并且返回权值最小的边。
然后删除最小边,加入新的边。
一旦当前图中边数==总顶点个数-1,就说明这是一颗生成树,更新一波最小值。
这个复杂度是O(n*m),官方题解说用并查集增删边可以使复杂度为O(mlogn)。
其实我一开始想的就是用并查集,用find可以飞速判断是否成环。
如果不进行路径压缩,貌似跟普通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也参与一下比较。