prim算法求最小生成树

【问题描述】
 已知含有n个顶点的带权连通无向图,采用邻接矩阵存储,邻接矩阵以三元组的形式给出,只给出不包括主对角线元素在内的下三角形部分的元素,且不包括不相邻的顶点对。请采用Prim算法,求该连通图从1号顶点出发的最小生成树的权值之和。
【输入形式】
 第一行给出结点个数n和三元组的个数count,以下每行给出一个三元组,数之间用空格隔开。(注意这里顶点的序号是从1到n,而不是0到n-1,程序里要小心!)
【输出形式】
 求解的最小生成树的各条边、边的权值之和
【样例输入】
 5 8
 2 1 7
 3 1 6
 3 2 8
 4 1 9
 4 2 4
 4 3 6
 5 2 4
 5 4 2
【样例输出】

1-3:6
3-4:6
4-5:2
4-2:4
18

【样例说明】
 权值是正整数,可能很大,但不需要考虑整型溢出问题

解法:

先将三元组转化为邻接矩阵,无向图的邻接矩阵为对称矩阵

m[i][j]=k代表从第i号结点到第j号结点的权值为k

769
7844
686
9464
44

tag代表是否被并入U集合,neighbour代表邻接顶点的编号,weight代表两个顶点间的权重

每一趟都将所有tag为0的点与选中顶点的权重表里原来存放的weight作比较,如果前者的值较小,则修改表里的值(红色代表被修改的值,蓝色代表被选中的顶点),如果两个值相同,不要修改。

修改操作完成之后,找到所有tag为0中的点对应的weight值最小的那个点,将他的tag置为1,也就是把他所对应的线并入最小生成树。

第一趟:

顶点编号12345
tag10000
neighbour1111
weight

第二趟:

顶点编号12345
tag10000
neighbour1111
weight769

第三趟:

顶点编号12345
tag10100
neighbour1131
weight766

第四趟:

顶点编号12345
tag10110
neighbour4134
weight4662

第五趟:

顶点编号12345
tag10111
neighbour4134
weight4662

第六趟:

顶点编号12345
tag11111
neighbour4134
weight4662

此时选中了n-1条边,循环结束。

表格里存放的便是最小生成树。

但是,题目这个遍历的顺序是dfs的顺序,所以我把这棵树又存进了邻接矩阵

00600
00040
60060
04602
00020

这样dfs比较方便

#include <iostream>
using namespace std;
const int N = 100;
const int Max = 99999;

class MST
{
public:
    int tag,weight,nb;
    MST():tag(0),weight(Max){}
};

void input(int m[][N], int n, int count)//初始化邻接矩阵
{
    for (int i = 0; i <= n; i++)
    {
        for (int j = 0; j <= n; j++)
        {
            m[i][j] = Max;
        }
    }
    for (int i = 0; i < count; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        //cout << a << ' ' << b << ' ' << c << endl;
        m[a][b] = c;
        m[b][a] = c;
    }
}

void prim(MST tr[],int m[][N], int n)
{
    int sum = 0, choice=1, wmin = Max, mmin;
    tr[1].tag = 1;

    while (sum < n-1)
    {
        wmin = Max;
        for (int i = 2; i <= n; i++)//修改
        {
            if (tr[i].tag == 0)
            {
                if (m[i][choice] < tr[i].weight)
                {
                    tr[i].weight = m[i][choice];
                    tr[i].nb = choice;
                }

                if (tr[i].weight < wmin)
                {
                    wmin = tr[i].weight;
                    mmin = i;
                }
            }
        }
        tr[mmin].tag = 1;
        choice = mmin;
        sum++;
    }
}

void MSTtrans(MST* tr, int m[][N],int n)
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= n; j++)
        {
            m[i][j] = 0;
        }
    }
    for (int i = 2; i <= n; i++)
    {
        m[i][tr[i].nb] = tr[i].weight;
        m[tr[i].nb][i] = tr[i].weight;
    }
}

void dfs(int m[][N],int visited[][N], int i, int n)
{
    for (int j = 1; j <= n; j++)
    {
        if (m[i][j] != 0 && visited[i][j] == 0)
        {
            cout << i << '-' << j << ':' << m[i][j] << endl;
            visited[i][j] = 1; visited[j][i] = 1;
            dfs(m, visited, j, n);
        }
    }
}

int main()
{
    int m[N][N]; int visited[N][N] = {0};
    int n,count;
    cin >> n >> count;
    //cout << n <<' '<< count<<endl;
    input(m, n, count);

    MST tr[N];
    prim(tr, m, n);
    MSTtrans(tr, m, n);
    dfs(m, visited, 1, n);
    int sum = 0;
    for (int i = 2; i <= n; i++)
    {
        sum += tr[i].weight;
    }
    cout << sum;
    return 0;
}

更新堆优化的版本

#include <iostream>
#include <queue>
using namespace std;
const int inf = 1e9 + 7;
int n, m, dis[51], vis[51], ans = 0;
int head[51], cnt = 0;

struct Node {
    int pos, w;
    Node(int p, int wt) :pos(p), w(wt) {}
    bool operator<(const Node& a)const {
        return w > a.w;
    }
};

struct Edge {
    int to, next, w;
}e[101];

void add_edge(int u, int v, int w) {
    ++cnt;
    e[cnt].to = v, e[cnt].w = w, e[cnt].next = head[u];
    head[u] = cnt;
}

void prim() {
    fill(dis + 1, dis + 1 + n, inf);
    priority_queue<Node> q;
    q.push({ 1,0 });
    while (q.size()) {
        Node tmp = q.top(); q.pop();
        if (!vis[tmp.pos]) {
            vis[tmp.pos] = 1; ans += tmp.w;
            for (int i = head[tmp.pos]; i; i = e[i].next) {
                int v = e[i].to, w = e[i].w;
                if (!vis[v]) {
                    if (w < dis[v]) {
                        dis[v] = w;
                        q.push({ v,dis[v] });
                    }
                }
            }
        }
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        add_edge(u, v, w);
        add_edge(v, u, w);
    }
    prim();
    cout << ans;
    return 0;
}

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值