一.最小生成树:连通N个点的边权值总和最小的树。
二.时间复杂度
Prim算法:时间复杂度O(|V|2+|E|),O(|E|log|V|)
Kruskal算法:时间复杂度O(|E|log|E|)
算法的选择: 从图的稀疏程度考虑(稠密图Prim,稀疏图Kruskal或Prim + Heap)
三.具体算法
1.Prim算法:(1) 任意选定一点s,设集合S={s}
(2) 从不在集合S的点中选出一个点j使得其与S内的某点i的距离最短,则(i,j)就是生成树上的一条边,同时将j点加入S
(3) 转到(2)继续进行,直至所有点都己加入S集合
Prim模板:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
#define Twhile() int T;scanf("%d",&T);while(T--)
#define ArrInit(a,b,n) for(int i=0;i<=n;i++)a[i]=b
#define ArrInit2(a,b,n,m) for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)a[i][j]=b
#define fora(i,a,b) for(int i=a;i<b;i++)
#define fors(i,a,b) for(int i=a;i>b;i--)
#define fora2(i,a,b) for(int i=a;i<=b;i++)
#define fors2(i,a,b) for(int i=a;i>=b;i--)
#define PI acos(-1.0)
#define eps 1e-6
#define INF 0x3f3f3f3f
typedef long long LL;
typedef long long LD;
using namespace std;
const int maxn=2000+11;
int ma[maxn][maxn];
int N,M;//点数,边数
int ans;//最小生成树的权值之和
//stack<int>S; //最小生成树加入的点的顺序
//int lowerPoint[maxn]; // i离S中的lowerPoint[i]最近
int lowerCost[maxn],use[maxn];//lowerCost离S的最小距离,use是否被加入S
void init()
{
ans=0;
ArrInit2(ma,INF,N,N);
ArrInit(use,0,N);
}
void primInit()
{
// S.push(1);
use[1]=1;
lowerCost[1]=0;
// lowerPoint[1]=1;
fora2(i,2,N)
{
// lowerPoint[i]=1;
lowerCost[i]=ma[1][i];
}
}
void prim()
{
primInit();
int n=N-1;
while(n--){
int k=-1,tem=INF;
fora2(i,2,N)//找到距离S集合最小的点
{
if(use[i])continue;
if(lowerCost[i]<tem)
{
tem=lowerCost[i];
k=i;
}
}
use[k]=1;
ans+=tem;
/*
lowerCost[k]=0;lowerPoint[i]=i; //不更新的话,当要求输出最小生成树的边可以直接输出
S.push(k);
*/
fora2(i,1,N)
{
if(use[i])continue;
int t=ma[i][k];
if(t<lowerCost[i])
{
lowerCost[i]=t;
//lowerPoint[i]=k;
}
}
if(tem==INF)return;//图不连通
}
}
int main()
{
while(~scanf("%d",&N)&&N)
{
scanf("%d",&M);
init();
while(M--)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
ma[x][y]=min(ma[x][y],z);
ma[y][x]=min(ma[y][x],z);
}
if(N==1){printf("0\n");continue;}
prim();
printf("%d\n",ans);
}
return 0;
}
2.Kruskal算法:(1) 将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边
(2) 最终得到的结果就是最小生成树
Kruskal模板:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
#define Twhile() int T;scanf("%d",&T);while(T--)
#define ArrInit(a,b,n) for(int i=0;i<=n;i++)a[i]=b
#define ArrInit2(a,b,n,m) for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)a[i][j]=b
#define fora(i,a,b) for(int i=a;i<b;i++)
#define fors(i,a,b) for(int i=a;i>b;i--)
#define fora2(i,a,b) for(int i=a;i<=b;i++)
#define fors2(i,a,b) for(int i=a;i>=b;i--)
#define PI acos(-1.0)
#define eps 1e-6
#define INF 0x3f3f3f3f
typedef long long LL;
typedef long long LD;
using namespace std;
const int maxn=5000+11;
int N,M,ans;//点,边,最小生成树
struct edge
{
int u,v;//点u到v
int var;//权值
}ma[maxn];
bool cmp(edge a,edge b)
{
return a.var<b.var;
}
int fa[maxn];
void initKruskal()
{
ans=0;
fora2(i,1,N)
{
fa[i]=i;
}
sort(ma+1,ma+M+1,cmp);
}
/*
Kruskal算法
(1) 将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边
(2) 最终得到的结果就是最小生成树
并查集
*/
int findx(int x)
{
if(x==fa[x])return x;
return fa[x]=findx(fa[x]);
}
bool unio(int x,int y)
{
int fx=findx(x),fy=findx(y);
if(fx==fy)return false;
fa[fy]=fx;
return true;
}
void kruskal()
{
initKruskal();
int m=0;
fora2(i,1,M)
{
if(unio(ma[i].u,ma[i].v))
{
m++;
ans+=ma[i].var;
//printf("%d %d %d\n",ma[i].u,ma[i].v,m);
}
if(m==N-1)return;
}
}