11.17 日誌
阿新 • • 發佈:2021-11-17
最小生成樹(MST)
對於稀疏圖:
1、樸素Prim演算法 時間複雜度\(O(n^2)\)
演算法分析:和樸素Dijkstra的演算法流程十分相似,定義集合S表示最小生成樹的集合,每次先找出集合外距離集合最近的點t,隨後再用t去更新其他點到集合的距離。
https://www.acwing.com/problem/content/860/
程式碼示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000") //#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define For(i,a,b) for (int i=(a);i<=(b);++i) #define Fod(i,b,a) for (int i=(b);i>=(a);--i) #define mls multiset #define lb lower_bound #define ub upper_bound #define pb push_back #define pob pop_back #define itt iterator #define endl '\n' #define IOS ios::sync_with_stdio(0); cin.tie(0); #define lowbit(x) x & (-x) #define clr(x) memset(x, 0, sizeof(x)); #define fi first #define se second typedef vector<int> vii; typedef vector<long long> vll; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int MAXN = 0x3f3f3f3f; const int MOD = 1000000007; const ll MOD1 = 212370440130137957ll; const int N = 505; const int M = 1e5 + 5; int g[N][N]; int n, m; int dist[N]; bool st[N]; int prim() { memset(dist, 0x3f, sizeof dist); int res = 0; for(int i = 0; i < n; i ++) { int t = -1; for(int j = 1; j <= n; j ++) if(!st[j] && (t == -1 || dist[j] < dist[t])) t = j; if(i && dist[t] == MAXN) return MAXN; if(i) res += dist[t]; st[t] = true; for(int j = 1; j <= n; j ++) //儲存完dist[t]的值之後再去更新,以防出現自環的情況 dist[j] = min(dist[j], g[t][j]); } return res; } int main () { //IOS; cin >> n >> m; memset(g, 0x3f, sizeof g); for(int i = 1; i <= m; i ++) { int a, b, c; cin >> a >> b >> c; g[a][b] = g[b][a] = min(g[a][b], c); } int t = prim(); if(t == MAXN) puts("impossible"); else cout << t << endl; return 0; } /* */
對於稠密圖:
1、Kruskal演算法 時間複雜度\(O(mlogm)\)
演算法分析:先將每個點看作一棵獨立分離的樹,之後先將所有邊按邊權從小到大進行排序,然後遍歷每條邊進行兩點的相連,若兩點連通則相連,否則不連,判斷方式可以用並查集。最終當連的邊數為n-1條時,即存在最小生成樹(所有點皆連通),過程中再用個res記錄下每條連線的邊的邊權和即可
https://www.acwing.com/problem/content/861/
程式碼示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000") //#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define For(i,a,b) for (int i=(a);i<=(b);++i) #define Fod(i,b,a) for (int i=(b);i>=(a);--i) #define mls multiset #define lb lower_bound #define ub upper_bound #define pb push_back #define pob pop_back #define itt iterator #define endl '\n' #define IOS ios::sync_with_stdio(0); cin.tie(0); #define lowbit(x) x & (-x) #define clr(x) memset(x, 0, sizeof(x)); #define fi first #define se second typedef vector<int> vii; typedef vector<long long> vll; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int MAXN = 0x7fffffff; const int MOD = 1000000007; const ll MOD1 = 212370440130137957ll; const int M = 2e5 + 5; const int N = 1e5 + 5; struct node { int a, b, c; bool operator < (const node &x) const { return c < x.c; } }e[M]; int n, m; int p[N]; int res; int cnt; int find(int x) { if(x == p[x]) return x; return p[x] = find(p[x]); } int main () { //IOS; cin >> n >> m; for(int i = 1; i <= m; i ++) { int a, b, c; cin >> a >> b >> c; e[i] = {a, b, c}; } sort(e + 1, e + 1 + m); for(int i = 1; i <= n; i ++) p[i] = i; for(int i = 1; i <= m; i ++) { int a = find(e[i].a), b = find(e[i].b); if(a != b) { p[b] = a; res += e[i].c; cnt ++; } } if(cnt != n - 1) puts("impossible"); else cout << res << endl; return 0; }
PS:而對於堆優化版的Prim演算法其實和堆優化Dijkstra一樣,但演算法實現上不如kruskal簡單,且時間複雜度差不多,因此不常用。