最小生成樹的兩個模板

  什麼是最小生成樹??當在一個圖中,這個圖描繪了節點與節點的關係,表現爲這個節點到下一個節點的 “代價” 那麼我們想把這個圖中的各個節點用最小的代價連接起來該怎麼辦呢??這裏就要知道一個關鍵詞,最小生成樹。  那麼是否有實際應用呢??
    例如:要在n個城市之間鋪設光纜,主要目標是要使這 n 個城市的任意兩個之間都可以通信,但鋪設光纜的費用很高,且各個城市之間鋪設光纜的費用不同,因此另一個目標是要使鋪設光纜的總費用最低。這就需要找到帶權的最小生成樹。
    好下面我們就直接貼代碼:在代碼中有註釋能直接更快的明白最小生成樹該怎麼寫!

代碼一:prim(普里姆)算法
/*
prim算法是一個不高效,並且佔用內存較爲高的一種最小生成樹算法。但是好理解。這樣呢也確實一種方法。
畢竟學習算法就是學習思想嘛!
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,mann[100][100],vis[100],dis[100];
//mann中存儲節點之間的距離,默認是最大值mx
//vis數組是標記節點是否已經被加入的了最小生成樹裏邊
//dis是記錄最小生成樹到各個未加入最小生成樹節點的距離
const int mx=1<<29;//上限,這個程序的最大值

int pri(){
    for(int j=1;j<=n;j++){
        dis[j]=mann[1][j];//初始化dis,以後會更新
    }
    memset(vis,0,sizeof(vis));//初始化vis,表示都未加入最小生成樹
    vis[1]=1;//表示節點1加入最小生成樹。
    int minn,to,sum=0;
    for(int i=1;i<n;i++){
        minn=mx;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&dis[j]<minn){//是否加入最小生成樹,並且是最小值
                minn=dis[j];
                to=j;//記錄最小生成樹到其他節點的代價最小的下標
            }
        }
        sum+=minn;//總代價+當前算出的小代價
        vis[to]=1;//表示當前找到最小節點加入最小生成樹
        for(0int j=1;j<=n;j++){
            if(!vis[j]){//判斷是否加入最小生成樹
                dis[j]=min(dis[j],mann[j][to]);//更新各個單獨節點到最小生成樹的最小距離
            }
        }
    }
    return sum;
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){//初始化某節點到某節點爲當前程序的極大值
                mann[i][j]=mx;
            }
        }
        int f,t,v;//表示f節點到t節點的代價爲v
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&f,&t,&v);
            mann[f][t]=v;
            mann[t][f]=v;
        }
        printf("%d\n",pri());
    }
    return 0;
}


    介紹完prim(普里姆)算法就該介紹 kruskal(克魯斯卡爾)算法了 ,kruskal算法涉及到了並查集的知識但是 如果並查集學得好 看一眼就會知道原理了。
代碼二:kruskal(克魯斯卡爾)算法

/*
    這個算法比起prim算法是比較節省空間的。並且感覺時間上也是很節省的。因爲是先排序再查找。
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,record[100];
//n,m   n代表節點的總數,m代表在節點間共有多少種代價。
//record代表各個節點到 “祖” 的距離,學過並查集都知道這個東西
struct node{
    int f,t,v;//f到t節點的代價爲v
}Array[100];
int cmp(node a,node b){
    return a.v<b.v;
}
int _find(int x){
    if(x!=record[x]){
        record[x]=_find(record[x]);//查找到 祖 值後進行路徑壓縮
    }
    return record[x];
}
int solve(){
    int sum=0,cnt=0;
    for(int i=1;i<=m;i++){
        int f=_find(Array[i].f);
        int t=_find(Array[i].t);
        if(f!=t){//在最小生成樹裏邊,都有着共同的 “祖” 值。沒有共同的祖值表示 未加入到最小生成樹裏邊
            record[f]=t;
            sum+=Array[i].v;//總代價增加
            cnt++;
        }
        if(cnt==n-1){//因爲如果把所有節點連上了後肯定需要n-1個邊
            return sum;
        }
    }
    return -1;
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++){
            record[i]=i;//初始化各個節點的  "祖"  值
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&Array[i].f,&Array[i].t,&Array[i].v);
        }
        sort(Array+1,Array+m+1,cmp);//對各個節點 代價 進行排序。按照從小到大
        printf("\n%d\n",solve());
    }
    return 0;
}



最後: Prim算法和Kruskal算法都能從連通圖找出最小生成樹。區別在於Prim算法是挨個找,而Kruskal是先排序再找。是不是感覺最小生成樹很簡單呢???當然如果你想要把 Prim算法和Kruskal算法合併的話可以嘗試用鏈表這個數據結構存儲節點–>排序–>像Kruskal算法那樣生成最小生成樹!!

心靈雞湯:
    既然選擇程序,就要匠心所致;
    既然選擇遠方,就要風雨兼程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章