最小生成树
最小生成树对应的图一般都是无向图
Prim 算法
朴素版 Prim 算法
时间复杂度:O ( n 2 ) O(n^{2}) O ( n 2 )
适合稠密图
Prim 算法求最小生成树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> #include <cstring> using namespace std ;const int N = 510 , INF = 0x3f3f3f3f ;int g[N][N];int d[N];bool st[N];int n, m;int prim () { memset (d, 0x3f , sizeof d); 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 || d[t] > d[j])) t = j; } if (i && d[t] == INF) return INF; st[t] = true ; if (i) res += d[t]; for (int j=1 ;j<=n;j++) { d[j] = min(d[j], g[t][j]); } } return res; } int main () { cin >> n >> m; memset (g, 0x3f , sizeof g); while (m--) { int a, b, w; cin >> a >> b >> w; g[a][b] = g[b][a] = min(g[a][b], w); } int t = prim(); if (t == INF) cout << "impossible" << endl ; else cout << t << endl ; return 0 ; }
堆优化 Prim 算法
时间复杂度:O ( m ⋅ l o g ( n ) ) O(m \cdot log(n)) O ( m ⋅ l o g ( n ) )
适合稀疏图,不太常用
Kruskal 算法
时间复杂度:O ( m ⋅ l o g ( m ) ) O(m \cdot log(m)) O ( m ⋅ l o g ( m ) )
适合稀疏图
算法步骤:
将所有边按照权重从小到大排序。O ( m ⋅ l o g ( m ) ) O(m \cdot log(m)) O ( m ⋅ l o g ( m ) )
枚举每条边。如果这条边的两个节点 a, b 不连通,将这条边加入最小生成树。
二分图
一个图是二分图 当且仅当图中不含奇数环 。
染色法
时间复杂度:O ( m + n ) O(m + n) O ( m + n )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <cstring> using namespace std ;const int N = 100010 , M = 200010 ;int h[N], e[M], ne[M], idx;int st[N];void add (int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++; } bool dfs (int u, int color) { st[u] = color; for (int i = h[u]; i != -1 ; i = ne[i]){ int j = e[i]; if (!st[j]) { if (!dfs(j, 3 - color)) return false ; } else if (st[j] == color) return false ; } return true ; } int main () { int n, m; scanf ("%d%d" , &n, &m); memset (h, -1 , sizeof h); while (m --){ int a, b; scanf ("%d%d" , &a, &b); add(a, b), add(b,a); } bool flag = true ; for (int i = 1 ; i <= n; i ++){ if (!st[i]){ if (!dfs(i, 1 )){ flag = false ; break ; } } } if (flag) puts ("Yes" ); else puts ("No" ); return 0 ; }
匈牙利算法
最坏时间复杂度:O ( m ⋅ n ) O(m \cdot n) O ( m ⋅ n )
实际运行时间远小于 O ( m ⋅ n ) O(m \cdot n) O ( m ⋅ n )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <iostream> #include <cstring> #include <algorithm> using namespace std ;const int N = 510 , M = 1e5 + 10 ;int n1, n2, m;int h[N], e[M], ne[M], idx;int match[N];bool st[N];void add (int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } bool find (int x) { for (int i=h[x];i!=-1 ;i=ne[i]) { int j = e[i]; if (!st[j]) { st[j] = true ; if (match[j] == 0 || find(match[j])) { match[j] = x; return true ; } } } return false ; } int main () { scanf ("%d%d%d" , &n1, &n2, &m); memset (h, -1 , sizeof h); while (m--) { int a, b; scanf ("%d%d" , &a, &b); add(a, b); } int res = 0 ; for (int i=1 ;i<=n1;i++) { memset (st, false , sizeof st); if (find(i)) res++; } printf ("%d\n" , res); return 0 ; }