题目:传送门
prim算法
思路:
用数组记录每个点到最小生成树所在集合的最小距离,每次选取距离最小的点以该距离加入集合,并用该点更新其他点的距离。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010, M = 200010;
int g[N][N], res, n, m;
int dis[N]; // dis[i]:当前使得第i个点加入集合的最小距离
bool in[N]; // 记录当前点是否被加入集合
bool prim()
{
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
for (int i = 1; i <= n; i++)
{
int t = -1;
for(int j = 1; j <= n; j++)
if(!in[j] && (t == -1 || dis[t] > dis[j])) t = j;
if (dis[t] == 0x3f3f3f3f) return false; // 若所有未加入集合的点到集合的距离均为无穷大,则不存在最小生成树
res += dis[t];
in[t] = true;
for(int j = 1; j <= n; j++)
dis[j] = min(dis[j], g[t][j]);
}
return true;
}
int main()
{
memset(g, 0x3f, sizeof g);
cin >> n >> m;
int x, y, z;
for(int i = 0; i < m; i++)
{
cin >> x >> y >> z;
g[y][x] = g[x][y] = min(g[x][y], z);
}
if(prim()) cout << res;
else cout << "orz";
return 0;
}
kruskal算法
思路:
将所有边按权重从小到大排序,然后遍历每条边,用并查集判断边的左右端点是否属于同一集合内,若不属于,则该边为最小生成树中的边,最后将左右端点所在集合合并。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010, M = 200010;
int f[N];
int n, m;
struct edge
{
int a, b, w;
bool operator < (const edge & t)
{
return w < t.w;
}
}edges[M];
inline int find(int x)
{
if(x != f[x]) f[x] = find(f[x]);
return f[x];
}
int main()
{
cin >> n >> m;
int x, y, z;
for(int i = 0; i < m; i++)
{
cin >> x >> y >> z;
edges[i] = {x, y, z};
}
sort(edges, edges+m);
for (int i = 1; i <= n; i++) f[i] = i;
int cnt = 0, ans = 0;
for(int i = 0; i < m; i++)
{
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if( a!= b)
{
cnt++;
ans += w;
f[a] = b;
}
}
if(cnt < n - 1) cout << "orz"; //仅当合并次数为n-1次,所有点才处于同集合中,否则最小生成树不存在
else cout << ans;
return 0;
}