本题和上一题一样是求最小生成树。
题目回忆
给定一个 个点 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。
给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。
由 中的全部 个顶点和 中 条边构成的无向连通子图被称为 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 的最小生成树。
题目分析
相较于 prim 算法对点进行搜索, kruskal 则是对边进行操作,这有点类似于 dijkstra 算法和 spfa 算法,不过 kruskal 算法的限制明显更小。
首先我们读入边的两个端点和边权,定义一个三元组(即用一个二元组,二元组的第二个元素仍旧是一个二元组),我们按 {边权,{端点a,端点b}} 的方式依次读进三元组,再使用 对其进行排序,主要是按边权进行排序。
然后我们依次按边权从小到大的顺序读取三元组的元素,利用并查集的方式看看是否可以将所有端点连接到一棵树上,由于边权是从小到大排序,则最终若可以得到最小生成树,所选择的 条边的边权和一定是最小。同时我们定义一个标记,在进行并查集操作的过程中,我们每次更改某个端点的父节点时,将标记自加 ,最终判断是否等于 来判断是否能形成最小生成树。
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
pair<int, pair<int, int>> g[N];
int p[N];
int n, m;
int res;
int find(int x)
{
return x == p[x] ? p[x] : p[x] = find(p[x]);
}
bool kruskal()
{
int cnt = 0;
for (int i = 0; i < m; i ++)
{
auto [w, x] = g[i];
int a = find(x.first), b = find(x.second);
if (a != b)
{
p[b] = a;
res += w;
cnt ++;
}
}
if (cnt != n - 1) return false;
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 0; i < m; i ++)
{
int a, b, w;
cin >> a >> b >> w;
g[i] = {w, {a, b}};
}
sort(g, g + m);
for (int i = 1; i <= n; i ++) p[i] = i;
if (kruskal()) cout << res;
else cout << "impossible";
return 0;
}