1. 程式人生 > >最小生成樹的兩種最基本的演算法

最小生成樹的兩種最基本的演算法

prim

/** @Cain*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=2005;
const int inf=1e9+5;
int edge[maxn][maxn];
int near[maxn];
int low[maxn];
int p,l,n;
void prim()
{
    memset(low,0,sizeof(low));
    int sum=0;
    for(int i=0;i<n;i++)
        near[i]=edge[0][i];
    low[0]=1;//先定好以哪個點開始建樹,這裡是以0
開始的,若以其他點則相應的位置就可以了. for(int i=0;i<n-1;i++){ int minn=inf; int v=-1; for(int j=0;j<n;j++){ if(!low[j] && near[j]<minn){ minn=near[j]; v=j; } } if(v!=-1){ sum+=minn; low[v]=1
; for(int j=0;j<n;j++){ if(!low[j] && edge[v][j]<near[j]) near[j]=edge[v][j]; } } } cout << sum << endl; } int main() { cin >> n; //因為輸入是一個矩陣. /*for(int i=0;i<n;i++){ //無向圖;有向圖把存臨接矩陣那改一下就可以了. int
u,v,w; scanf("%d %d %d",&u,&v,&w); edge[u-1][v-1]=edge[v-1][u-1]=w; } for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(i==j) edge[i][j]=0; else if(edge[i][j]==0) edge[i][j]=inf; } }//這個是輸入為點-點-邊的存法.*/ for(int i=0;i<n;i++){//無向圖;有向圖把存臨接矩陣那改一下就可以了. for(int j=0;j<n;j++){ scanf("%d",&edge[i][j]); } }//這個是輸入為矩陣的存法. /*for(int i=0;i<n;i++){//列印鄰接矩陣. for(int j=0;j<n;j++){ printf("%d ",edge[i][j]); } printf("\n"); }*/ prim();//所以prim即適用於矩陣輸入,也適用於邊輸入.只是邊輸入時用kruskal更簡單. } /* 3 0 1 2 1 0 3 2 3 0//這個是矩陣的存法. 3 1 2 1 1 3 2 2 3 3//這個是kruskal的存法. */

kruskal

//這個是有兩種寫法,如果單獨算最小生成樹,則節點 最好選其 本身 ,如果加上判環那些則節點最好選為-1,在此都貼下 相應程式碼.

節點為本身的

/** @Cain*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m;
int pre[maxn];
struct node
{
    int u,v,w;
    node(int u=0,int v=0,int w=0):u(u),v(v),w(w){};
    bool operator < (const node &a) const {
        return a.w>w;
    }
}s[maxn];

int Find(int x)
{
    return pre[x]==x?x:pre[x]=Find(pre[x]);
}

void solve()
{
    while(~scanf("%d",&n)){
        if(!n) break;
        scanf("%d",&m);
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].w);
        }
        for(int i=0;i<=n;i++) pre[i] = i;
        sort(s,s+m);
        int ans = 0,num=0;
        for(int i=0;i<m;i++){
            int u = Find(s[i].u),v = Find(s[i].v);
            if(u != v){
                pre[u] = v;
                ans += s[i].w;
                num++;
            }
            if(num == n-1) break;
        }
        printf("%d\n",ans);
    }
}

//有時候節點為-1時題目的要求會好實現的多! 所以還是要學到!!! 至少要記得有這種方式 !!!
節點為-1的.

/** @Cain*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5;
int n,m;
int fa[maxn];
struct node
{
    int u,v,w;
    node(int u=0,int v=0,int w=0):u(u),v(v),w(w){};
    bool operator < (const node &a) const {
        return a.w>w;
    }
}s[maxn];

int Find(int x)
{
    return fa[x]<0?x:fa[x]=Find(fa[x]);
}

void Union(int x,int y)
{   int m=Find(x);
    int n=Find(y);
    int tmp=fa[m]+fa[n];
    if(fa[m]>fa[n]){//總是讓節點少的加到節點大的那棵樹上去.
        fa[m]=n;
        fa[n]=tmp;
    }
    else{
        fa[n]=m;
        fa[m]=tmp;
    }
}
void kruskal()
{
    int ans=0;//儲存總長度.
    int sum=0;//已用邊條數
    memset(fa,-1,sizeof(fa));
    for(int i=0;i<m;i++){
        int u=s[i].u,v=s[i].v;
        if(Find(u)!=Find(v))
        {
            ans+=s[i].w;
            sum++;
            Union(u,v);
        }
        if(sum>=n-1) break;;
    }
    printf("ans = %d\n",ans);
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=0;i<m;i++){
        scanf("%d %d %d",&s[i].u,&s[i].v,&s[i].w);
    }
    sort(s,s+m);
    kruskal();
}