一、什么是最小生成树
最小生成树(Minimum Spanning Tree,MST):
在一个给定的无向图G中求一棵树T,
- 树T拥有图G的所有顶点,
- 所有边都来自图G,
- 使得整棵树的边权最小
贪心策略:
prim算法: 让小树长大
kruskal算法:将森林合并成树
二、prim算法
与Dijkstra算法区别:
思想几乎完全相同,
Dijkstra算法的最短距离指到源点s的最短距离;
prim算法的最短距离指到集合s的最短距离
时间复杂度是O(V^2),
邻接表实现通过堆优化可使时间复杂度降为O(VlogV+E)
尽量在稠密图上使用
三、程序示例
//邻接矩阵版
//求边权之和ans
#include <iostream>
#define MAXN 510
#define MAXDATA 1000000000
using namespace std;
int G[MAXN][MAXN];
int nv,ne,c1;//顶点数,边数,起始点
int ans;//最小生成树的边权之和
int dis[MAXN];
int collected[MAXN];
void prim(){
fill(dis,dis+nv,MAXDATA);
dis[c1]=0;
for(int i=0;i<nv;i++){
//1:FindMin
int minId=-1,minValue=MAXDATA;
for(int j=0;j<nv;j++){
if(!collected[j] && dis[j]<minValue){
minValue=dis[j];
minId=j;
}
}
if(minId==-1) break;//如果生成树存在,不会中途退出
collected[minId]=1;
ans+=dis[minId];//不知道是哪条边,总之是该点到集合s最近的边
//2:main part
for(int j=0;j<nv;j++){
if(!collected[j] && G[minId][j]!=MAXDATA){
if(dis[j]>G[minId][j]){
//不需要经过minId了,只需要到minId最短
dis[j]=G[minId][j];
}
}
}
}
}
int main(){
scanf("%d %d %d",&nv,&ne,&c1);//顶点数,边数,起始点
for(int i=0;i<nv;i++){
for(int j=0;j<nv;j++){
G[i][j]=MAXDATA;
}
}
int tmp1,tmp2,tmpl;
for(int i=0;i<ne;i++){
scanf("%d %d %d",&tmp1,&tmp2,&tmpl);
G[tmp1][tmp2]=tmpl;
G[tmp2][tmp1]=tmpl;//无向图
}
prim();
printf("%d",ans);
return 0;
}
//邻接表版
#include <iostream>
#include <vector>
#define MAXN 510
#define MAXDATA 1000000000
using namespace std;
struct Node
{
int v;
int dis;
Node(int _v,int _dis):v(_v),dis(_dis){}
};
vector<Node> Adj[MAXN];//邻接表
int nv,ne,c1;//顶点数,边数,起始点
int ans;//最小生成树的边权之和
int dis[MAXN];
int collected[MAXN];
void prim(){
fill(dis,dis+nv,MAXDATA);
dis[c1]=0;
for(int i=0;i<nv;i++){
//1:FindMin
int minId=-1,minValue=MAXDATA;
for(int j=0;j<nv;j++){
if(!collected[j] && dis[j]<minValue){
minValue=dis[j];
minId=j;
}
}
if(minId==-1) break;
collected[minId]=1;
ans+=dis[minId];//不知道是哪条边,总之是该点到集合s最近的边
//2:main part
for(int j=0;j<Adj[minId].size();j++){//邻接点
int v=Adj[minId][j].v;
int dist=Adj[minId][j].dis;
if(!collected[v] && dis[v]>dist){//未收录且需要更新
dis[v]=dist;
}
}
}
}
int main(){
scanf("%d %d %d",&nv,&ne,&c1);//顶点数,边数,起始点
int tmp1,tmp2,tmpl;
for(int i=0;i<ne;i++){
scanf("%d %d %d",&tmp1,&tmp2,&tmpl);
Adj[tmp1].push_back(Node(tmp2,tmpl));
Adj[tmp2].push_back(Node(tmp1,tmpl));
}
prim();
printf("%d",ans);
return 0;
}
测试数据(起始点为0):
6 10 0
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
输出结果:15