最小生成树——Prim算法及应用

讲真,去年学理论的时候就觉得普里姆算法与迪杰斯特拉太像了,一个是最小生成树,一个是最短路径,但用的思路都是在余下的当中找与当前现有的距离最短的。
与迪杰斯特拉伪代码不同之处在于:
将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

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值