最小生成樹

最小生成樹是樹的一種,具體有兩種算法去實現。ios

普利姆(prim)算法:web

         大意:先找一個數(通常爲1或輸入的第一個數)將其視爲一個集合m,那麼剩下的集合可看作(u(設爲總集合)-m)。算法

接着找出一個最短邊(在鏈接兩個集合的邊中),而後將這邊納入集合m,直到m=u;測試

           例子:spa

       

某省調查鄉村交通情況,獲得的統計表中列出了任意兩村莊間的距離。省政府「暢通工程」的目標是使全省任何兩個村莊間均可以實現公路交通(但不必定有直接的公路相連,只要能間接經過公路可達便可),並要求鋪設的公路總長度爲最小。請計算最小的公路總長度。
code

 

Inputorm

測試輸入包含若干測試用例。每一個測試用例的第1行給出村莊數目N ( < 100 );隨後的N(N-1)/2行對應村莊間的距離,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間的距離。爲簡單起見,村莊從1到N編號。
當N爲0時,輸入結束,該用例不被處理。
排序

 

Outputstring

對每一個測試用例,在1行裏輸出最小的公路總長度。
it

 

Sample Input

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

 

Sample Output

3
5

code:

#include <stdio.h>
#include <string.h>
#define MaxInt 65535//設置最大值 表示兩個點不通
#define N 110
int map[N][N],low[N],visited[N];
int n;
int prim()
{
    int i,j,pos,min,result=0;
    memset(visited,0,sizeof(visited));
    visited[1]=1;//遍歷過就標記爲一
    pos=1;//記錄m集合的「外交官」
    for(i=1; i<=n; i++)
        if(i!=pos)
            low[i]=map[pos][i];//記錄和pos點邊距離的值

    for(i=1; i<n; i++)//固然要算的是整個頂點
    {
        min=MaxInt;
        for(j=1; j<=n; j++)
            if(visited[j]==0&&min>low[j])
            {
                min=low[j];
                pos=j;
            }//找出最短邊
        result+=min;
        visited[pos]=1;//歸入m集合.
        for(j=1; j<=n; j++)
            if(visited[j]==0&&low[j]>map[pos][j])
                low[j]=map[pos][j];//記錄和pos點邊距離的值
    }
    return result;
}
int main()
{
    int i,v,j,ans,a,b;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
            break;
        memset(map,MaxInt,sizeof(map));//初始值 視每兩個點都不通
        for(j=1; j<=n*(n-1)/2; j++)
        {
            scanf("%d%d%d",&a,&b,&v);
            map[a][b]=map[b][a]=v;//標記兩個城市間的距離
        }
        ans=prim();
        printf("%d\n",ans);
    }
    return 0;
}
2 克魯斯卡爾(kruskal)算法:

  大意:

咱們假設一個圖有m個節點,n條邊。首先,咱們須要把m個節點當作m個獨立的生成樹,而且把n條邊按照從小到大的數據進行排列。在n條邊中,咱們依次取出其中的每一條邊,若是發現邊的兩個節點分別位於兩棵樹上,那麼把兩棵樹合併成爲一顆樹;若是樹的兩個節點位於同一棵樹上,那麼忽略這條邊,繼續運行。等到全部的邊都遍歷結束以後,若是全部的生成樹能夠合併成一條生成樹,那麼它就是咱們須要尋找的最小生成樹,反之則沒有最小生成樹。ps 要用並查集的思想

例題:

       

某省調查鄉村交通情況,獲得的統計表中列出了任意兩村莊間的距離。省政府「暢通工程」的目標是使全省任何兩個村莊間均可以實現公路交通(但不必定有直接的公路相連,只要能間接經過公路可達便可),並要求鋪設的公路總長度爲最小。請計算最小的公路總長度。

 

Input

測試輸入包含若干測試用例。每一個測試用例的第1行給出村莊數目N ( < 100 );隨後的N(N-1)/2行對應村莊間的距離,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間的距離。爲簡單起見,村莊從1到N編號。
當N爲0時,輸入結束,該用例不被處理。

 

Output

對每一個測試用例,在1行裏輸出最小的公路總長度。

 

Sample Input

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

 

Sample Output

3
5

code:

  #include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define M 5000
int Start[M],End[M],Wealth[M],father[M],line[M];

int find_(int f)//查找父節點
{
    if(f!=father[f])
        return father[f]=find_(father[f]);
    return f;
}

int cmp(const int i,const int j)//sort排序要加的東西,具體啥用不清楚。。。
{
    if(Wealth[i]!=Wealth[j])
       return Wealth[i]<Wealth[j];//按邊值排序

}

int kruskal(int n,int m)//具體實現步驟
{
    int e,ans=0,i,j,x,y;
    for(i=0;i<=n;i++)//初始化爲單一集合
        father[i]=i;
    for(i=0;i<=m;i++)
        line[i]=i;
    sort(line,line+m,cmp);//按邊值排序
    for(i=0;i<m;i++)
    {
        e=line[i],x=find_(Start[e]),y=find_(End[e]);

        if(x!=y)//判斷是否兩個節點位於同一個樹上
        {
            father[x]=y;
            ans+=Wealth[e];
        }
    }
    return ans;
}

int main()
{
    int n,m,i,p;
    while(1)
    {
        scanf("%d",&n);
        if(n==0)
            break;
        m=n*(n-1)/2;
        for(i=0;i<m;i++)
            scanf("%d%d%d",&Start[i],&End[i],&Wealth[i]);
        p=kruskal(n,m);
        printf("%d\n",p);
    }
}

總結:

1.普里姆(Prim)算法
特色:時間複雜度爲O(n2).適合於求邊稠密的最小生成樹。
2.克魯斯卡爾(Kruskal)算法
特色:時間複雜度爲O(eloge)(e爲網中邊數),適合於求稀疏的網的最小生成樹。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息