1. 程式人生 > >『最小生成樹』prim & Kruskal

『最小生成樹』prim & Kruskal

amp truct turn get out const 貪心 方法 稠密圖

Kruskal和prim都是求最小生成樹的方法,兩種方法都是按照貪心來做的。但是Kruskal是從邊的角度入手,prim則是從點的角度入手。prim運用類似Dijkstra的方法來求,Kruskal運用並查集來求。

在復雜度方面,兩種算法各有所長。

在稀疏圖中,枚舉邊的Kruskal效率更高,在稠密圖中,枚舉點的prim效率更高。



Kruskal算法

按照邊權從小到大排序,每次判斷當前邊兩邊的點是否連通,如果沒有連通,就加入這條邊。

連通性我們可以用並查集維護。

復雜度為\(O(m*log_m+m)\)


附上代碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<queue>
using namespace std;
struct nod{
    int f,t,w;
};
nod edge[200005];
int fa[5005];
int n,m;
inline int cmp(nod a,nod b){
    return a.w<b.w;
}
inline int getfa(int a){
    return fa[a]==a?a:fa[a]=getfa(fa[a]);
}
inline void unio(int a,int b){
    fa[getfa(a)]=getfa(b);
}
int main(){
    std::ios::sync_with_stdio(false);
    for(register int i=0;i<=5000;i++){
        fa[i]=i;
    }
    cin>>n>>m;
    for(register int i=1;i<=m;i++){
        cin>>edge[i].f>>edge[i].t>>edge[i].w;
    }
    sort(edge+1,edge+m+1,cmp);
    int ans=0;
    for(register int i=1;i<=m;i++){
        if(getfa(edge[i].f)!=getfa(edge[i].t)){
            unio(edge[i].f,edge[i].t);
            ans+=edge[i].w;
        }
    }
    cout<<ans<<endl;
    return 0;
}



prim算法

prim算法的核心思想是枚舉到當前樹距離最近的點,然後加入到當前的樹中。每次找到最近的沒有在樹中的點,然後加入數中,更新最近距離。

復雜度為\(O(n^2)\),當然也可以優化。


附上代碼

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=5005;
const int INF=0x3f3f3f3f;
int n,m,cnt=1,ans=0;
int map[maxn][maxn],dis[maxn],fr[maxn];
bool vis[maxn];
inline void prim(){
    vis[1]=1,dis[1]=0,fr[1]=1;
    for(register int i=1;i<=n;i++)dis[i]=map[1][i];
    for(register int i=1;i<=n;i++){
        int mi=INF,v=-1;
        for(register int j=1;j<=n;j++)
            if(!vis[j]&&dis[j]<mi)mi=dis[j],v=j;
        if(v!=-1){
            cnt++;
            vis[v]=1;
            ans+=dis[v];
            for(register int j=1;j<=n;j++){
                if(vis[j])continue;
                if(dis[j]>map[v][j]){
                    dis[j]=map[v][j];
                    fr[j]=v;
                }
            }
        }
    }
}
int main(){
    memset(map,0x3f,sizeof(map));
    scanf("%d%d",&n,&m);
    for(register int i=1,x,y,z;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        map[x][y]=map[y][x]=min(map[x][y],z);
    }
    prim();
    cout<<ans<<endl;
}

『最小生成樹』prim & Kruskal