# 背景给定一个 n n n 个点 m m m 条边的无向图 ,图中可能存在重边和自环,边权可能为负数 求最小生成树 的树边权重之和,如果最小生成树不存在则输出 impossible
最小生成树 给定一张边带权的无向图 G = ( V , E ) G=(V,E) G = ( V , E ) ,其中 V V V 表示图中点的集合,E E E 表示图中边的集合,n = ∣ V ∣ n=|V| n = ∣ V ∣ ,m = ∣ E ∣ m=|E| m = ∣ E ∣ 由 V V V 中的全部 n n n 个顶点和 E E E 中的 n − 1 n-1 n − 1 条边构成的无向连通子图被称为 G G G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G 的最小生成树
# 数据范围1 ≤ n ≤ 500 1 \le n \le 500 1 ≤ n ≤ 5 0 0 1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1 ≤ m ≤ 1 0 5 # 思路P r i m Prim P r i m 算法用于解决稀疏图 的最小生成树,与 D i j k s t r a Dijkstra D i j k s t r a 算法十分相似 进行 n n n 次循环,每次找出 == 不在 “集合”== 内的,距离集合距离最小 的点
如果这个点不为第一个点,并且此点的距离到 “集合” 不为 0x3f3f3f3f
(初始化 d[]
为 0x3f3f3f3f
),则 res += d[t]
如果这个点 d[t] == 0x3f3f3f3f
,则证明此点无法抵达集合,即无法生成最小生成树 注意 :应先累加 res
再更新其他点的距离 d[t]
C++ 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 int prim () { int res = 0 ; for (int i = 1 ; i <= n; i++) { int t = -1 ; for (int j = 1 ; j <= n; j++) if (!st[j] && (t == -1 || d[j] < d[t])) t = j; if (i != 1 && d[t] == 0x3f3f3f3f ) return 0x3f3f3f3f ; if (i != 1 ) res += d[t]; st[t] = 1 ; for (int j = 1 ; j <= n; j++) d[j] = min (d[j], g[t][j]); } return res; } void solve () { memset (g, 0x3f , sizeof g), memset (d, 0x3f , sizeof d); cin >> n >> m; for (int i = 1 , a, b, c; i <= m; i++) { cin >> a >> b >> c; g[a][b] = g[b][a] = min (g[a][b], c); } int res = prim (); if (res == 0x3f3f3f3f ) cout << "impossible\n" ; else cout << res << endl; }
与 D i j s k t r a Dijsktra D i j s k t r a 的区别便在于这:
C++ 1 2 for (int j = 1 ; j <= n; j++) d[j] = min (d[j], g[t][j]);
D i j s t r a Dijstra D i j s t r a 是存第 i i i 点到源点的最小距离 d[j] = min(d[j], d[t] + g[t][j])
由于此时 t t t 点已经在集合里,所以 p r i m e prime p r i m e 是存第 i i i 点到集合的最小距离 d[j] = min(d[j], g[t][j])