在最小生成树问题里,正边和负边都没问题
朴素版prim算法 时间复杂度O(n^2)
生成树:每一次选中的t点,它和集合的距离对应的那条边,就是生成树的一条边
算法流程和dijkstra算法非常相似
cpp
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510,INF = 0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];
bool vis[N];
int prim(){
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
int res = 0;
for(int i = 1; i <= n; i ++ ){
int t = -1;
for(int j = 1; j <= n; j ++ ){
if(!vis[j] && (t == -1 || dist[j] < dist[t])){
t = j;
}
}
vis[t] = true;
if(dist[t] == INF) return 0;
//res的更新要先于dist[t]的更新,因为如果出现负环,就可能导致dist[t]被错误更新,从而导致res的错误
res += dist[t];
for(int j = 1; j <= n; j ++ ){
/* 与dijkstra
dist[j] = min(dist[j],dist[t] + g[t][j]);
不同的是,prim是与g[t][j]作比较,
因为dijkstra的dist[j]表示的是j与原点的最短距离,而prim算法中
dist[j]表示的是j点与集合的最短距离 */
dist[j] = min(dist[j],g[t][j]);
}
vis[t] = true;
}
return res;
}
int main(){
cin >> n >> m;
memset(g, 0x3f, sizeof g);
while(m -- ){
int u,v,w;
cin >> u >> v >> w;
//无向图是特殊的有向图,建边时只要建一条从a到b的,再建一条从b到a的就可以了
g[u][v] = g[v][u] = min(g[u][v],w);
}
int t = prim();
if(!t) cout << "impossible" << endl;
else cout << t << endl;
return 0;
}
堆优化版prim几乎不会用到,一般直接用kruskal就可以解决。堆优化的prim对比kruskal没有明显优势,还比较难写,故此处不贴模板。