1. 程式人生 > >最小生成樹簡述及模板題

最小生成樹簡述及模板題

先補充一點圖論的基本術語(基本複製於維基百科)

1.一個圖G頂點集(點集)一般記作V(G),當不發生混淆時可簡記為V。圖G為其頂點數目,亦即|V(G)|。以兩個頂點u

v為端點的邊一般記作(u,v)\{u,v\}uv。一條邊連線兩個頂點uv時,稱uv相鄰。圖G的邊集一般記作E(G),當不發生

混淆時可簡記為E

2.一個自環是兩個端點為同一頂點的邊。如果有多於一條邊連線同一對頂點,則它們均被稱為重邊。一個圖的重數是重複次數最多

的邊的重複次數。如果一個圖不含自環或重邊,則稱為簡單圖。多數情況下,如無特殊說明,可以假定“圖”總是指簡單圖。

3.連通圖:在無向圖中,若任意兩個頂點vivi與vjvj都有路徑相通,則稱該無向圖為連通圖。

4.強連通圖:在有向圖中,若任意兩個頂點vivi與vjvj都有路徑相通,則稱該有向圖為強連通圖。

5.生成樹:在無向圖中,n個頂點只取n-1條邊。生成樹是連通圖的極小聯通子圖,即如果再加一條邊,必形成環

6.最小生成樹:在所有生成樹中,邊代價和最小的生成樹

最小生成樹演算法

包括prime演算法和kruskal演算法

一.prime演算法,又稱“加點法”

    1.基本思想:在一個帶權連通圖中,任取一頂點加入生成樹中,標記,遍歷所有點,找到離該點距離最近的點,標記,直至所

有頂點都加入到生成樹中。(跟dijkstar思想很像)

    2.複雜度為O(n^2),n為頂點數,用鄰接表存,適用於稠密圖

    3.上一個模板題   杭電1233  還是暢通工程  http://acm.hdu.edu.cn/showproblem.php?pid=1233

    程式碼:

#include <bits/stdc++.h>

using namespace std;
const int mod = 1e9 + 7;
const int maxn = 110;
const int inf=0x3f3f3f3f;
typedef long long ll;
int mapp[maxn][maxn];
bool vis[maxn];
int dis[maxn];
int n;
int prime(int start){
    int ans=0;
    for(int i=1; i<=n; i++){
        dis[i]=mapp[start][i];//記錄起點出發的距離
        vis[i]=0;
    }
    vis[start]=1;   //加入任一點(或起點)
    int noww;
    for(int i=1; i<n; i++){
        int minn=inf;
        for(int j=1; j<=n; j++){
            if(!vis[j] && dis[j]<minn){//找到距離最短的
                minn=dis[j];
                noww=j;
            }
        }
        vis[noww]=1;
        ans+=minn;
        for(int j=1; j<=n; j++){
            if(!vis[j] && dis[j]>mapp[noww][j])//加入新的點後更新距離
                dis[j]=mapp[noww][j];
        }
    }
    return ans;
}
int main() {
    int u,v,d;
    while(~scanf("%d",&n)){
        if(n==0)  break;
        for(int i=1; i<=n; i++){
            for(int j=1; j<=n; j++){
                mapp[i][j]=inf;
            }
        }
        for(int i=1; i<= n*(n-1)/2; i++){
            scanf("%d%d%d",&u,&v,&d);
            mapp[u][v]=d;
            mapp[v][u]=d;
        }
        int ans=prime(1);
        printf("%d\n",ans);
    }
    return 0;
}

二.kruskal演算法,又稱“加邊法”

    1.基本思想:將邊按權值從小到大排序,遍歷,若該邊兩個端點不在一個樹上,則加上,若在,則跳過繼續下一條邊,貪心思

想,並查集實現

    2.時間複雜度O(mlogm),m為邊數

    3.還是上一個題

    程式碼:

#include <bits/stdc++.h>

using namespace std;
const int mod = 1e9 + 7;
const int maxn = 1e4+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m;
struct node{
    int u,v,val;
}s[maxn];
int fa[maxn],deep[maxn];
bool cmp(node x,node y){
    return x.val<y.val;
}
void init(){
    for(int i=0; i<=m; i++){
        fa[i]=i;
        deep[i]=1;
    }
}
int findd(int x){
    return fa[x]==x? x:x=findd(fa[x]);//查詢
}
void Union(int x,int y){
    int fx=findd(x);
    int fy=findd(y);
    if(deep[x]<deep[y])
        fa[fx]=fy;
    else{
        if(deep[x]==deep[y])  deep[x]++;
        fa[fy]=fx;
    }
}
int kruskal(){
    int ans=0;
    init();
    for(int i=1; i<=m; i++){
        if(findd(s[i].u) != findd(s[i].v)){
            Union(s[i].u,s[i].v);
            ans+=s[i].val;
        }
    }
    return ans;
}
int main() {
    while(~scanf("%d",&n)){
        if(n==0)  break;
        m = n*(n-1)/2;
        for(int i=1; i<=m; i++){
            scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].val);
        }
        sort(s+1,s+1+m,cmp);
        int ans=kruskal();
        printf("%d\n",ans);
    }
    return 0;
}