题目描述
根据输入创建无向网。分别用Prim算法和Kruskal算法构建最小生成树。(假设:输入数据的最小生成树唯一。)
输入
顶点数n
n个顶点
边数m
m条边信息,格式为:顶点1 顶点2 权值
Prim算法的起点v
输出
输出最小生成树的权值之和
对两种算法,按树的生长顺序,输出边信息(Kruskal中边顶点按数组序号升序输出)
输入样例
6
v1 v2 v3 v4 v5 v6
10
v1 v2 6
v1 v3 1
v1 v4 5
v2 v3 5
v2 v5 3
v3 v4 5
v3 v5 6
v3 v6 4
v4 v6 2
v5 v6 6
v1
输出样例
15
prim:
v1 v3 1
v3 v6 4
v6 v4 2
v3 v2 5
v2 v5 3
kruskal:
v1 v3 1
v4 v6 2
v2 v5 3
v3 v6 4
v2 v3 5
#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;
#define N 1000
class edge {
// 边类
public:
int start, end;
int dis;
edge(int start, int end, int dis)
{ this->start = start, this->end = end, this->dis = dis; }
};
// 顶点个数 边条数 状态数组
int n, m, sta[N] = {}, ans_sum = 0;
string ss[N], start;
// 边集 答案集
vector<edge> v, ans;
bool com(edge a, edge b) { return a.dis < b.dis; }
int find(string s) {
for (int i = 0; i < n; i++) { if (ss[i] == s) return i; }
return -1; // 不会返回-1
}
// prim算法
void prim() {
sta[find(start)] = 1; // 现将初始点加入点集
int con = 1;
while (n - con > 0) {
// 找最小点和边 a, b分别为边的起点和终点
int min_dis = INT_MAX, a, b;
for (int i = 0; i < v.size(); i++) {
if (sta[v[i].start] && !sta[v[i].end]) {
min_dis = v[i].dis, a = v[i].start, b = v[i].end;
break;
}
else if (!sta[v[i].start] && sta[v[i].end]) {
min_dis = v[i].dis, a = v[i].end, b = v[i].start;
break;
}
}
sta[b] = 1;
ans.push_back(edge(a, b, min_dis));
ans_sum += min_dis;
con++;
}
}
// 并查集
int find(int x) {
// if: 找到 就返回x
if (sta[x] == x) {
return x;
}
// 递归调用
return sta[x] = find(sta[x]);
}
// 克鲁斯卡尔算法 边集
void kruskal() {
// 依然是用sta表示状态数组
// 每个点一开始的尾点都是指向自己
int con = 1;
for (int i = 0; i < n; i++) { sta[i] = i; }
for (int i = 0; i < v.size() && n - con > 0; i++) {
// 遍历边集
if (find(v[i].start) != find(v[i].end)) {
// 不是相同的尾点就可以加入答案集合
// 题目要求小的点在前 = = ...
if (v[i].start < v[i].end) {
ans.push_back(v[i]);
// 松弛
sta[find(v[i].start)] = find(v[i].end);
}
else {
// relax(v[i].end, v[i].start);
ans.push_back(edge(v[i].end, v[i].start, v[i].dis));
sta[find(v[i].end)] = find(v[i].start);
}
con++;
}
}
}
int main() {
cin >> n;
// 输入顶点名称
for (int i = 0; i < n; i++) { cin >> ss[i]; }
// 输入边数
cin >> m;
string tem1, tem2;
int dis, a, b;
for (int i = 0; i < m; i++) {
cin >> tem1 >> tem2 >> dis;
a = find(tem1), b = find(tem2); // 找到这两个节点所在下标
v.push_back(edge(a, b, dis)); // 将边加入边集
}
cin >> start; // 初始点
sort(v.begin(), v.end(), com); // 从小到大排序
prim();
cout << ans_sum << endl;
cout << "prim:" << endl;
for (auto e : ans) {
cout << ss[e.start] << " " << ss[e.end] << " " << e.dis << endl;
}
ans.clear();
kruskal();
cout << "kruskal:" << endl;
for (auto e : ans) {
cout << ss[e.start] << " " << ss[e.end] << " " << e.dis << endl;
}
return 0;
}