最小生成树
对于图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;
}