①克鲁斯卡尔算法(Kruskal)
n是顶点数,m是边数;
cnt是已连的边数,如果已连的边数=总点数-1,即可跳出。
#include<bits/stdc++.h>
#define MAX 1005
using namespace std;
int p[MAX],n,m;
struct edge{
int x,y,w;
}a[MAX];
int find(int r)
{
if(p[r]!=r)
p[r]=find(p[r]);
return p[r];
}
void join(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy)
p[fx]=fy;
}
void init()
{
for(int i=0;i<=MAX;i++)
p[i]=i;
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int kruskal()
{
sort(a,a+m,cmp);
int cnt=0,cost=0,i;
for(i=0;i<m;i++)
{
int fx=find(a[i].x),fy=find(a[i].y);
if(fx!=fy)
{
p[fx]=fy;
cost+=a[i].w;
cnt++;
}
if(cnt==n-1)break;
}
return cost;
}
int main()
{
int d,i;
while(cin>>n>>m)
{
init();
for(i=0;i<m;i++)
{
scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].w,&d);
if(d==1)//将已有的路连起来
join(a[i].x,a[i].y);
}
cout<<kruskal()<<endl;
}
return 0;
}
②普里姆算法(Prim)
(1) n是顶点数,m是边数;
(2) edge[i][j]表示边e=(i,j)的权值,不存在的情况下设为INF,所以要记得提前做赋值操作;
(3) mincost[i]数组表示已访问点的集合到每个未访问点 i 的最小权值;
(4) vis[i]表示顶点 i 是否已访问
<1> 基础prim算法
#include<bits/stdc++.h>
#define MAX 1005
#define INF 0x3f3f3f3f
using namespace std;
int n,m,edge[MAX][MAX],mincost[MAX],vis[MAX];
void init()
{
int u,v,w,i;
memset(edge,INF,sizeof(edge));
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
edge[u][v]=edge[v][u]=w;
}
for(i=1;i<=n;i++)
{
vis[i]=0;
mincost[i]=INF;
}
mincost[1]=0;
}
int prim()
{
int i,v,cost=0;
init();
while(1)
{
v=-1;
for(i=1;i<=n;i++)
if(!vis[i]&&(v==-1||mincost[i]<mincost[v]))
v=i;
if(v==-1)
break;
vis[v]=1;
cost+=mincost[v];
for(i=1;i<=n;i++)
mincost[i]=min(mincost[i],edge[i][v]);
}
return cost;
}
int main()
{
cin>>n>>m;
cout<<prim()<<endl;
return 0;
}
<2> prim之记录并输出每次加入的边
#include<bits/stdc++.h>
#define MAX 1005
#define INF 0x3f3f3f3f
using namespace std;
int n,m,edge[MAX][MAX],mincost[MAX],vis[MAX];
void init()
{
int u,v,w,i;
memset(edge,INF,sizeof(edge));
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
edge[u][v]=edge[v][u]=w;
}
for(i=1;i<=n;i++)
{
vis[i]=0;
mincost[i]=INF;
}
mincost[1]=0;
}
void prim()
{
int i,v,start,end,sum=0,minn;
init();
while(1)
{
v=-1;
for(i=1;i<=n;i++)
if(!vis[i]&&(v==-1||mincost[i]<mincost[v]))
v=i;
if(v==-1)
break;
vis[v]=1,sum++;
minn=INF;
for(i=1;i<=n;i++)
{
mincost[i]=min(mincost[i],edge[i][v]);
if(minn>mincost[i]&&!vis[i])
{
minn=mincost[i];
end=i;
}
}
if(sum==n)
continue;
minn=INF;
for(i=n;i>=1;i--)
{
if(minn>=edge[i][end]&&vis[i])
{
minn=edge[i][end];
start=i;
}
}
printf("%d %d %d\n",start,end,mincost[end]);
}
}
int main()
{
cin>>n>>m;
prim();
return 0;
}

#include<bits/stdc++.h>
#define MAX 1005
#define INF 0x3f3f3f3f
using namespace std;
int n,m,edge[MAX][MAX],mincost[MAX],vis[MAX];
void init()
{
int u,v,w,i;
memset(edge,INF,sizeof(edge));
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
edge[u][v]=edge[v][u]=w;
}
for(i=1;i<=n;i++)
{
vis[i]=0;
mincost[i]=INF;
}
mincost[1]=0; //必须先确保下标的这个点1是存在的!
}
int prim()
{
int i,v,cost=0;
init();
while(1)
{
/*--------------------循环A---------------------*/
/*
PS:点v是距已访问点集合最近的未访问点
1、若点i是进循环A以来,第一个未被访问过的点,则无条件保存到点v
2、若点i不是第一个未被访问过的点,但点i距离已访问点集合更近,则保存到点v
*/
v=-1;
for(i=1;i<=n;i++)
if(!vis[i]&&(v==-1||mincost[i]<mincost[v]))
v=i;
/*--------------------循环A---------------------*/
//如果所有点都被访问过了,那么v还是-1,则会跳出这个大循环
if(v==-1)
break;
//将访问到的最近点v标记为已访问状态
vis[v]=1;
//总距离增加
cost+=mincost[v];
/*--------------------循环B---------------------*/
/*
PS:mincost[i]是已访问点集合到每个未访问点i的最小权值
1、若新增的点v距离某个未访问点i相较于之前更近
则将 已访问点集合到i的距离 更新为edge[i][v]
2、否则保持原样
*/
for(i=1;i<=n;i++)
mincost[i]=min(mincost[i],edge[i][v]);
/*--------------------循环B---------------------*/
}
return cost;
}
int main()
{
cin>>n>>m;
cout<<prim()<<endl;
return 0;
}
③ 总结
Kruskal在所有边中不断寻找最小的边,Prim在U和V两个集合之间寻找权值最小的连接。
它们的共同点在于:构造过程中都不能形成环!
(1) 时间上:
Prim适合稠密图,复杂度为O(n*n),因此通常使用邻接矩阵储存;
Kruskal适合稀疏图,多用邻接表储存,复杂度为O(e*loge)。
(2) 空间上:
Prim适合边较多的情况;
Kruskal适合点较多的情况。
来源:https://www.cnblogs.com/kannyi/p/8587733.html
