最小生成树:Prim

16 篇文章 0 订阅
9 篇文章 0 订阅

最小生成树

对于图G=<V,E>和图G’=<V’,E’>,如果G’是无向图且是树且V’=V,E’⊆E,则称G’为G的生成树

一张有权无向连通图中边权和最小的生成树叫做最小生成树

Prim

维护一个顶点集合C,每次通过一条边连接一个不在C中的点,直到最后形成一个树形结构。

定义dist(u)表示连接C中任意点和u的边的边权的最小值,我们把dist(u)简称为u到C的距离。

将任意点x加入C,此时C中只有一个点x;对于其他点y,如果x,y之间有边,则dist(y)等于这些边的边权的最小值,否则dist(y)等于无穷大;

在每一轮中,将dist最小(不能是无穷大)的还不在C中的顶点z加入C,同时将连接C和z的边权最小的边加入最小生成树的边集,并且用z连出去的边尝试更新其他点的dist;

当没有新的点加入C的时候,算法结束。此时如果所有的点都加入了C,则说明我们找到了最小生成树,否则说明图不连通。

时间复杂度: O ( n 2 + m ) O(n^2+m) O(n2+m)

邻接表

typedef pair<int, int> PII;
vector<PII> edge[N];
int n, m, dist[N];
bool b[N];
int Prim()
{
    memset(b, false, sizeof b);
    memset(dist, 127, sizeof dist);
    dist[1] = 0;
    int ans = 0, tot = 0;
    while (true)
    {
        int x = -1;
        for (int i = 1; i <= n; i++)
            if (!b[i] && dist[i] < 1 << 30)
                if (x == -1 || dist[i] < dist[x])
                    x = i;
        if (x == -1)
            break;
        ++tot;
        ans += dist[x];
        b[x] = true;
        for (auto i : edge[x])
            dist[i.first] = min(dist[i.first], i.second);
    }
    if (tot != n)
        return -1;
    else
        return ans;
}

邻接矩阵

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int n;       // n表示点数
int g[N][N]; // 邻接矩阵,存储所有边
int dist[N]; // 存储其他点到当前最小生成树的距离
bool st[N];  // 存储每个点是否已经在生成树中

// 如果图不连通,则返回INF(值是0x3f3f3f3f), 否则返回最小生成树的树边权重之和
int prim()
{
    memset(dist, 0x3f, sizeof dist);

    int res = 0;
    for (int i = 0; i < n; i++)
    {
        int t = -1;
        for (int j = 1; j <= n; j++)
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        if (i && dist[t] == INF)
            return INF;

        if (i)
            res += dist[t];
        st[t] = true;

        for (int j = 1; j <= n; j++)
            dist[j] = min(dist[j], g[t][j]);
    }

    return res;
}

Prim优化

set优化

时间复杂度: O ( ( n + m ) l o g n ) O((n+m)log n) O((n+m)logn)

typedef pair<int, int> PII;
const int N = 5e3 + 10;
const int M = 1e4 + 10;
set<PII> q;
vector<PII> edge[N];
int n, m, dist[N];
bool b[N];
int Prim()
{
    memset(b, false, sizeof b);
    memset(dist, 127, sizeof dist);
    dist[1] = 0;
    q.clear();
    for (int i = 1; i <= n; i++)
        q.insert({dist[i], i});
    int ans = 0, tot = 0;
    while (!q.empty())
    {
        int x = q.begin()->second;
        q.erase(q.begin());
        if (dist[x] > 1 << 30)
            break;
        ++tot;
        ans += dist[x];
        b[x] = true;
        for (auto i : edge[x])
        {
            if (!b[i.first] && i.second < dist[i.first])
            {
                q.erase({dist[i.first], i.first});
                dist[i.first] = i.second;
                q.insert({dist[i.first], i.first});
            }
        }
    }
    if (tot != n)
        return -1;
    else
        return ans;
}

priority_queue优化

const int INF = 0x3f3f3f3f;
typedef pair<int, int> PII;

vector<PII> edge[N];
int n, m, dist[N];
bool st[N];

int Prim()
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, false, sizeof st);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    for (int i = 1; i <= n; i++)
        heap.push({dist[i], i});

    int ans = 0, tot = 0;
    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver])
            continue;
        ++tot;
        ans += dist[ver];
        st[ver] = true;

        for (auto i : edge[ver])
        {
            if (!st[i.first] && i.second < dist[i.first])
            {
                dist[i.first] = i.second;
                heap.push({dist[i.first], i.first});
            }
        }
    }

    if (tot != n)
        return -1;
    return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值