Prim算法
核心思路
从图G<V,E>选取任意一个点为起始点,加入集合S中。在除S外的点集V-S中,找与集合S有最短距离的结点(假设为u),把u作为中介点,优化其他顶点到集合S的最短距离。
(一句话,找结点--->未访问过且使该结点到S距离最短,优化其他点)
伪代码:
int Prim(G,dis[])
{
初始化
for(循环n-1次) //找到权值最小的n-1条边
{
for(遍历所有结点) //找未被访问过的结点u,u到集合S的距离最短(打擂台的方法)
记u为已访问过
for(遍历所有结点) //以u为中介点优化V-S中的结点到S的距离dis[],假设u可达的结点为v
{ if(v未被访问过&&dis[v]>G[u][v])
dis[v]=G[u][v];
}
}
}
具体实现的代码:
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=100;
bool visited[MAXN]={false};
class MGraph
{
public:
MGraph(int a[],int n,int e);
~MGraph(){}
int Prim(int v);//返回最小生成树的所有边的权值之和,即生成树的代价
private :
int vertexnum,edgenum;
int edge[MAXN][MAXN];
int vertex[MAXN];
};
MGraph::MGraph(int a[],int n,int e)
{
vertexnum=n;
edgenum=e;
int p,q,w;
for(int i=1;i<=n;i++)
vertex[i]=a[i];
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++)
edge[i][j]=INF;//假设所有的点都是不连通的,所以彼此不可达,距离为INF
for(int i=1;i<=e;i++)
{
//cout<<"请输入存在边的两个结点及其权值";
cin>>p>>q>>w;
//注意此处的输入,用于简单图,即无平行边,如果有平行边,原来的边长会被覆盖。
//所以非简单图可以加个判断语句保留两结点之间最短的边。
edge[p][q]=edge[q][p]=w;
}
}
int MGraph::Prim(int v)//假定从v结点开始访问
{
int dis[vertexnum+2];
for(int i=1;i<=vertexnum;i++)
dis[i]=INF;
for(int i=1;i<=vertexnum;i++)
dis[i]=edge[v][i];//初始化其他顶点到S集合的距离
int ans=0; //统计权值之和
dis[v]=0; //!!!起始结点到集合S的距离为0
visited[v]=true; //将v结点加入S集合
for(int k=1;k<vertexnum;k++) //注意!循环n-1次,找n-1条边
{
int MIN=INF,u=-1;
for(int i=1;i<=vertexnum;i++)
{
//找与S集合有最短距离的点u;
if(visited[i]==false&&dis[i]<MIN)
{
MIN=dis[i],u=i;
}
}
//cout<<u<<" ";
if(u==-1) return -1;//图不连通,不能形成树
visited[u]=true;//将结点u加入S集合
ans+=dis[u];
for(int i=1;i<=vertexnum;i++)
{
if(visited[i]==false&&dis[i]>edge[i][u]) //以u为中介点优化其他顶点到S集合之间的距离
dis[i]=edge[i][u];
}
}
return ans;
}
int main()
{
int n,e;
cin>>n>>e;
int a[n+2];
for(int i=0;i<=n;i++)
a[i]=i;
MGraph Tree(a,n,e);
int ans=Tree.Prim(1);
if(ans==-1) cout<<"orz";
else cout<<ans;
}
总结:
(1)与Dijkstra的区别,在于记录距离的数组dis[]含义不同,Prim是优化未访问的结点Vi 到集合S的距离,Dijkstra是优化未访问的结点Vi 到起始结点s的距离
(2)常用于稠密图