DS图—最小生成树

题目描述

根据输入创建无向网。分别用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;
}


  • 11
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值