#include<iostream>
#include<cstring>
using namespace std;
const int N = 5010, INF = 0x3f3f3f3f;
int n, m;
int a[N][N]; // 连接矩阵
int dist[N]; // 标记各个点距离生成树的最短距离
bool st[N]; // 标记哪个点已经加入到生成树当中
int prim()
{
//初始化
memset(dist, 0x3f3f3f3f, sizeof dist);
dist[1] = 0;
int ret = 0; // 统计最终结果
for (int i = 1; i <= n; i++) // 把所有的点加入到生成树中
{
//找最近的点
int t = 0;
for (int j = 1; j <= n; j++) // dist[]遍历找
{
if (dist[j] < dist[t] && !st[j]) t = j;
}
// 找到了此时的最短距离的点
// 判断是不是连通图
if (dist[t] == INF) return INF; // 最短距离是无穷大就代表这个点与树不是连通的
st[t] = true;
ret += dist[t];
//更新最短距离
for (int j = 1; j <= n; j++) // 枚举t能走到哪------t是起始顶点
{
dist[j] = min(dist[j], a[t][j]);
}
}
return ret;
}
int main()
{
cin >> n >> m;
memset(a, 0x3f3f3f3f, sizeof a);
for (int i = 1; i <= m; i++)
{
int x, y, z; cin >> x >> y >> z;
//连接矩阵每个格子只能存1个数据,所以就要在这先把重边问题
//解决掉
a[x][y] = a[y][x] = min(a[x][y], z);
}
int ret = prim();
if (ret == INF) cout << "orz" << endl;
else cout << ret << endl;
return 0;
}
2、vector + prim算法:
cpp复制代码
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef pair<int, int> PII; // <结束顶点,边权>
const int N = 5010, INF = 0x3f3f3f3f;
int n, m;
vector<PII> a[N]; // 该起始顶点有哪些结束顶点,她两的边权是多少
int dist[N]; // 此时各个顶点距离生成树的最小距离
bool st[N]; // 标记哪些顶点已经加入到生成树当中
int prim()
{
//初始化
memset(dist, 0x3f3f3f3f, sizeof dist);
dist[1] = 0;
int ret = 0; // 最终结果
for (int i = 1; i <= n; i++) // 期待把n个点加入生成树中
{
//找最近的点 --- 找dist数组中值最小的,要他下标,下标就是点
int t = 0;
for (int j = 1; j <= n; j++)
{
if (dist[j] < dist[t] && !st[j]) t = j;
}
// 找到一个此时意义上的最短距离点,通过其距离判断是否连通
if (dist[t] == INF) return INF; // 不连通
st[t] = true;
ret += dist[t];
//更新最短距离
for (auto& e : a[t]) // 看作为起始顶点的t能走到哪个结束顶点
{
// a 结束顶点;b 边权
int a = e.first, b = e.second; // 对于结束顶点,b 是新的一个可以选择的值,
//适合的话可以替换掉
// t -> a 权值是b
dist[a] = min(dist[a], b); // 跟新此时结束顶点到生成树的最短距离
}
}
return ret;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int x, y, z; cin >> x >> y >> z;
//可以存多个,在跟新最短距离的时候解决重边
a[x].push_back({ y, z });
a[y].push_back({ x, z });
}
int ret = prim();
if (ret == INF) cout << "orz" << endl;
else cout << ret << endl;
return 0;
}
2、kruskal算法
kruskal算法的核心是不断加边,当已有边加完之后,看看计数变量的值是不是 n - 1,是 就是构建成功,不是 就是构建失败,构建的是非连通图。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5010, M = 2e5 + 10, INF = 0x3f3f3f3f;
int n, m;
struct node
{
int x, y, z;
}a[M];
int fa[N]; // 并查集管理已连通的边
bool cmp(node& a, node& b)
{
return a.z < b.z;
}
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int kruskal()
{
// 1、排序
sort(a + 1, a + 1 + m, cmp);
int cnt = 0; // 计数变量
int ret = 0; // 结果变量
for (int i = 1; i <= m; i++) // 所有边都要考虑
{
int x = a[i].x, y = a[i].y, z = a[i].z;
int fx = find(x), fy = find(y); // 找各自集合的父亲
if (fx != fy) // 判断父亲是否相同 -不同-两端点不连通
{
cnt++;
ret += z;
fa[fx] = fy; // 把已连通的端点放在一个集合维护
}
//if (find(x) != find(y))
//{
// cnt++;
// ret += z;
// fa[find(x)] = find(y);
//}
}
return cnt == n - 1 ? ret : INF;
//if (cnt != n - 1) return INF;
//else return ret;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++) cin >> a[i].x >> a[i].y >> a[i].z;
//并查集初始化
for (int i = 1; i <= n; i++) fa[i] = i;
int ret = kruskal();
if (ret == INF) cout << "orz" << endl;
else cout << ret << endl;
return 0;
}