首先,我们来说一下图当中的最小生成树,对于一个无向图来说,它的最小生成树的定义如下:
1,该数经过所有的结点
2,经过的所有的结点的边权之和最小
3,这棵树的起点可以是任意的点,不过一般都是题目中指定的
prim算法:
适用范围:边多点少的稠密图。
复杂度:O(v^2)
与Dijkstra算法相比,该算法的区别是里面的d数组,Dijkstra算法的d表示的是当前的结点v距离起始节点s的最短距离,而该算法的d表示的是当前结点和它的上一个结点之间的最短距离。我们的Dijkstra算法里面的起点在我们的prim算法中是一个集合S,prim算法的思路是对边进行一个贪心,逐个逐个边进行贪心,最后得到最优解。
代码:
const int maxn=2020;
const int INF=1000000000;
int G[maxn][maxn],d[maxn];
bool vis[maxn]={false};
int prim(int s){
fill(d,d+maxn,INF);
d[s]=0;
int ans=0;
for(int i=0;i<ans;i++){
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<MIN){
MIN=d[j];
u=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]<d[v]&&G[u][v]!=INF){
d[v]=G[u][v]; //更新路径长度
}
}
}
return ans;
}
kurskal算法:
适用范围:点多边少的稀疏图
复杂度:O(ElogE),E为图的边数
该算法主要是通过从整体进行一个贪心的策略,检查所有的边,找出权最小的边进行存储。
但是,还有两个前提:
1,如果加入这条边之后与已经加入的边形成一个连通图,也就是会形成环,那么就不能把这条边加入。
2,如果此时的边的数量等于结点数量-1的话,那么就可以直接退出循环了。
主要需要解决的是两个问题:
1,怎么确定加上这条边后会不会是一个连通图?这个可以用之前学过的并查集先进行处理,然后后面要用到的时候再进行判断。
2,如何保存我们的加入的边?还是用到并查集,通过并查集中的合并的函数来进行处理。
代码:
const int maxn=100;
struct edge{
int u,v;
int cost;
}E[maxn];
bool cmp(edge a,edge b){
return a.cost<b.cost;
}
int father[maxn];
int n,m;
int findfather(int x){ //并查集和路径压缩
int a=x;
whiel(x!=father[x]){
x=father[x];
}
while(a!=father[a]){
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
int kruskal(int n,int m){
int ans=0,Num_edge=0;
for(int i=0;i<n;i++){ //初始化
father[i]=i;
}
sort(E,E+m,cmp);
for(int i=0;i<m;i++){
int fau=findfather([E[i].u]);
int fav=findfather([E[i].v]);
if(fau!=fav){ //如果边的两边的点不是连通块
father[fau]=fav;
ans+=E[i].cost;
Num_edge++;
if(Num_edge==n-1) break;
}
}
if(Num_edge!=n-1) return -1;
else return ans;
}