讲真,去年学理论的时候就觉得普里姆算法与迪杰斯特拉太像了,一个是最小生成树,一个是最短路径,但用的思路都是在余下的当中找与当前现有的距离最短的。
与迪杰斯特拉伪代码不同之处在于:
:将G[u][v]赋值给v与集合s的最短距离d[v];,也就是说,这里的d[v]表示顶点v与集合最短的距离。而在迪杰斯特拉算法中,d[]数组表示的是某个顶点到原点的最短路径距离。
伪代码:
//G为图,一般设成全局变量;数组d为顶点与集合s的最短距离
Prim (G,d[]){
初始化;
for(循环n次){
u = 使d[u]最小的还未被访问的顶点的标号;
记u已被访问;
for(从u出发能到达的所有顶点v){
if(v未被访问 && 以u为中介点使得v与集合s的最短距离d[v]更优){
将G[u][v]赋值给v与集合s的最短距离d[v];
}
}
}
}
邻接矩阵版:
const int maxn = 1000;
const int INF = 1000000000;
int n,G[maxn][maxn];
int d[maxn];
bool vis[maxn] = {false};
int prim(){
fill(d,d+maxn,INF);
d[0] = 0;
int ans = 0;
for(int i = 0;i <n;i++){
int u = -1,MIN = INF;
for(int j = 0;j <n;j++){
if(vis[j] == false && d[j] <MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return -1;
vis[u] = true;
ans += d[u]; //将与集合s距离最小的边加入最小生成树
for(int v = 0;v <n;v++){
if(vis[v] == false && G[u][v] != INF && G[u][v] <d[v]){
d[v] = G[u][v]; //将G[u][v]赋值给d[v]
}
}
}
return ans; //返回最小生成树的边权之和
}
邻接表版:
const int maxn = 1000;
const int INF = 1000000000;
struct Node{
int v,dis;
};
vector<Node>Adj[maxn];
int n;
int d[maxn]; //顶点与集合s的最短距离
bool vis[maxn] = {false};
int prim(){
fill(d,d+maxn,INF);
d[0] = 0;
int ans = 0; //存放最小生成树的边权之和
for(int i = 0;i <n;i++){
int u = -1,MIN = INF;
for(int j = 0;j <n;j++){
if(vis[j] == false && d[j]<MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return -1;
vis[u] = true;
ans += d[u]; //将与集合s距离最小的边加入最小生成树
for(int j = 0;j <Adj[u].size();j++){
int v = Adj[u][j].v;
if(vis[v] == false && Adj[u][j].dis <d[v]){
d[v] = Adj[u][j].dis; //将Adj[u][j].dis赋值给d[v]
}
}
}
return ans;
}
亚历山大攻打恶魔大陆的案例:
《算法笔记》P407
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn = 1000;
const int INF = 1000000000;
int n,m,G[maxn][maxn];
int d[maxn];
bool vis[maxn] = {false};
int prim(){
fill(d,d+maxn,INF);
d[0] = 0;
int ans = 0;
for(int i = 0;i <n;i++){
int u = -1,MIN = INF;
for(int j = 0;j <n;j++){
if(vis[j] == false && d[j]<MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return -1;
vis[u] = true;
ans += d[u];
for(int v = 0;v <n;v++){
if(vis[v] == false && G[u][v] != INF && G[u][v] <d[v]){
d[v] = G[u][v];
}
}
}
return ans;
}
int main(){
int u,v,w;
scanf("%d%d",&n,&m);
fill(G[0],G[0] + maxn*maxn,INF);
for(int i = 0;i <m;i++){
scanf("%d%d%d",&u,&v,&w);
G[u][v] = G[v][u] = w; //无向图
}
int ans = prim();
printf("%d\n",ans); //连接图上所有顶点,经过的边权最短路径和
return 0;
}
/*
6 10
0 1 4
0 4 1
0 5 2
1 2 6
1 5 3
2 3 6
2 5 5
3 4 4
3 5 5
4 5 3
*/