prim算法 O(n2)
普利姆算法适用于稠密图,思路与朴素版本的dijkstra很像,都是找到距离最近的点,然后用该点去更新邻边。
不同的是:
- dij找的是该点到源点的距离;而prim找的是该点到集合的最短距离,这里的集合指的是某些点所连成的连通块, 我们要找的最小生成树就是把这些所有的点都连接在一起,使得这些边的权值之和最小。
- 更新方式不同,由于更新方式的不同使得寻找最优解的方式也不同,在prim算法中就是一层一层的拓展更新,可能之前所连点的距离被后面的更新所取代,但dij就不一样了,dij每次确定一个点后就一直往前走,找到最小的边,每次用到源点距离最短的点更新邻边, dij更新的是 某点到源点的最短, prim更新的是某点到集合的最短距离,在确定某个点到源点 / 集合的最短距离后,要用该点去更新其邻边,dij这样更新
d[j] = min(d[j], d[t] + g[t][j]);
prim这样更新:d[j] = min(d[j], g[t][j]);
两者的g[t][j]都表示的是该点与邻点边权的大小. - 求最小生成树要用一个变量加权统计,那么什么时候加?
在确定某个点到集合最短距离之前,如果该确定点到集合的距离不是INF, 就加上,如果是INF说明所找到的与集合距离最近的点距离为INF,该点不连通,最小生成树肯定就不存在
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510;
int g[N][N], d[N];
bool st[N];
int n, m;
int res;
bool prim()
{
memset(d, 0x3f, sizeof d);
d[1] = 0;
for(int i = 0; i < n; i ++ )
{
int t = -1;
for(int j = 1; j <= n; j ++ )
if(!st[j] && (t == -1 || d[t] > d[j]))
t = j;
if(d[t] == 0x3f3f3f3f) return false;
res += d[t];
st[t] = true;
for(int j = 1; j <= n; j ++ )
d[j] = min(d[j], g[t][j]);
}
return true;
}
int main()
{
cin >> n >> m;
memset(g, 0x3f, sizeof g);
while (m -- )
{
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b], c);
}
if(prim()) cout << res << endl;
else puts("impossible");
return 0;
}
kruskal算法O(m)
kruskal算法用于求解稀疏图的最小生成树
思路:
- 将所有的边读入结构体中,按边权排序
- 初始化并查集数组p[],用于查看两个点是否在一个集合中
- 枚举所有的边,每次拿出最小的边,查询两个边的两个端点是否在一个集合中,如果不在一个集合中就加入到一个集合中(加边)同时记录res加边权,也要记录一下枚举完所有的边一共加了多少边。如果加边数小于n-1,说明可能存在自环或不连通,最小生成树不存在
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10, M = N * 2;
int n, m;
int res, cnt; //cnt为边数
int p[N];
struct Edge{
int a, b, w;
bool operator < (const Edge &W)const
{
return w < W.w;
}
}edges[M];
int find(int x)
{
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
bool kruskal()
{
for(int i = 0; i < m; i ++ )
{
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if(a != b)
{
p[a] = b;
res += w;
cnt ++;
}
}
if(cnt < n - 1) return false;
return true;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ ) p[i] = i;
for(int i = 0; i < m; i ++ )
{
int a, b, c;
cin >> a >> b >> c;
edges[i] = {a, b ,c};
}
sort(edges, edges + m);
if(kruskal()) cout << res << endl;
else puts("impossible");
return 0;
}