最小生成树
一个定理:必定包含权值最小的边
不然把小边加入,删除环上任何一条边结果都更优
两个算法:
- Kruskal
枚举边判断点
贪心算法,每次判断最短的边加入是否会形成环,用并查集判断
可以加就加入
时间复杂度\(O(mlogm)\)
const int N = 200010 ;
const int M = 500010 ;
int n, m ;
int fa[N] ;
struct edge {
int x, y, z ;
friend bool operator < (const edge &a, const edge &b) {
return a.z < b.z ;
}
} a[M] ;
int get(int x) {
if (x == fa[x]) return x ;
return fa[x] = get(fa[x]) ;
}
void merge(int x, int y) {
fa[x] = y ;
}
int main() {
scanf("%d%d", &n, &m) ;
for (int i = 1; i <= m; i++) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z) ;
sort(a + 1, a + m + 1) ;
for (int i = 1; i <= n; i++) fa[i] = i ;
ll ans = 0 ;
for (int i = 1; i <= m; i++) {
int x = get(a[i].x), y = get(a[i].y) ;
if (x == y) continue ;
merge(x, y) ;
ans += a[i].z ;
}
printf("%lld\n", ans) ;
}
- Prim
适用于完全图
时间复杂度\(O(N^2)\)
维护点加入边
维护两个集合\(S\)和\(T\),一个是已经在树中的,还有一个是还没加入的
加边时只加入一个点在\(S\)中,一个点在\(T\)中这样的边,确保没有环出现
实际操作时是 使新加入\(S\)中的点\(x\),去更新还在\(T\)中的点\(y\)的权值(用边权)
利用标记分开\(S\)和\(T\)两个部分
int n, m ;
int a[N][N], d[N], v[N] ;
void prim() {
memset(d, 0x3f, sizeof(d)) ;
memset(v, 0, sizeof(v)) ;
d[1] = 0 ;
for (int rnd = 1; rnd < n; rnd++) {
int x = 0 ;
for (int i = 1; i <= n; i++) if (!v[i] && (x == 0 || d[x] > d[i])) x = i ;
v[x] = 1 ;
for (int y = 1; y <= n; y++) if (!v[y]) d[y] = min(d[y], a[y][x]) ;
}
}
int main() {
scanf("%d%d", &n, &m) ;
memset(a, 0x3f, sizeof(a)) ;
for (int i = 1; i <= m; i++) {
int x, y, z ; scanf("%d%d%d", &x, &y, &z) ;
a[x][y] = a[y][x] = min(a[x][y], z) ;
}
prim() ;
ll ans = 0 ;
for (int i = 1; i <= n; i++) ans += d[i] ;
printf("%lld\n", ans) ;
}
例题
走廊泼水节
link
模拟\(kruskal\)加边的方式
\(kruskal\)每次取出最短的边,把两个节点所在的树连接在一起
设子树\(1\)的大小为\(s1\),子树\(2\)的大小为\(s2\)
对于所有子树\(1\)中的一个节点\(s\)和子树\(2\)中的一个节点\(t\),连接\(s\)和\(t\)的边的长度必须要长于 当前这条边的长度\((val)\)
这样的边数总共有\(s1*s2-1\)条,边长\(val+1\)
而且\(val+1\)已经可以取到的最短的长度(后面加的边不会影响\(1\)棵子树内部的结果)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std ;
typedef long long ll ;
const int N = 1000010 ;
struct Edge {
int x, y, val ;
friend bool operator < (const Edge &a, const Edge &b) {
return a.val < b.val ;
}
} edge[N] ;
int n, T ;
ll ans = 0 ;
int fa[N], sze[N] ;
void init() {
for (int i = 1; i <= n; i++) fa[i] = i ;
for (int i = 1; i <= n; i++) sze[i] = 1 ;
ans = 0 ;
}
int get(int x) {
if (x == fa[x]) return x ;
return fa[x] = get(fa[x]) ;
}
void merge(int x, int y) {
fa[x] = y ;
sze[y] += sze[x] ;
}
int main() {
scanf("%d", &T) ;
while (T--) {
scanf("%d", &n) ;
for (int i = 1; i < n; i++) scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].val) ;
init() ;
sort(edge + 1, edge + n) ;
for (int i = 1; i < n; i++) {
int x = edge[i].x, y = edge[i].y, val = edge[i].val ;
int fx = get(x), fy = get(y) ;
ans += 1ll * (val + 1) * (sze[fx] * sze[fy] - 1) ;
merge(fx, fy) ;
}
printf("%lld\n", ans) ;
}
}

浙公网安备 33010602011771号