還是暢通工程(最小生成樹)

問題描述

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

 

輸入

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

 

輸出

對每個測試用例,在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


 

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

連通並且最短距離,顯然是最小生成樹問題。

最小生成樹問題有兩種基本解決算法:

(1)prim算法(加點法)

(2)kruskal算法(加邊法)

 

法一:prim算法

    加點法。

   初始化點1,設置爲已訪問visit[1]=1。將每個未訪問點到訪問點的最短距離初始化爲到點1 的距離,dis[j]=map[1][j]。

   不斷選擇未訪問到訪問點的最短距離dis[n],加入最短距離,將最短距離的點設置爲已訪問。

   重新計算每個未訪問點的最短距離。

   對n-1個點進行選擇。(n-1次循環)

 

#include <stdio.h>
#include <string.h>
#define MAXN 100000
#define N 110

int map[N][N];
int visit[N];
int dis[N];
int prime(int n)
{
    int i,j,min,k,s=0;
    memset(visit,0,sizeof(visit));
    for(i=1;i<=n;i++)
        dis[i]=map[1][i];
    visit[1]=1;
    for(i=1;i<n;i++)      //n-1個點
    {
        min=MAXN;
        for(j=1;j<=n;j++)
        {
            if(visit[j]==0&&dis[j]<min)    //從1出發選擇一條最短的路
            {
                min=dis[j];
                k=j;
            }
        }
        visit[k]=1;                        //k也訪問過了,更新所有未訪問過的最短距離
        s=s+dis[k];
        for(j=1;j<=n;j++)
        {
            if(visit[j]==0&&dis[j]>map[k][j])
            {
                dis[j]=map[k][j];
            }
        }
    }
    return s;
}

int main()
{
    int n,i,j,x,y,s,m,sum=0;
    while(scanf("%d",&n))
    {
        if(n==0) break;
        for(i=0;i<=n;i++)
            for(j=0;j<=n;j++)
                map[i][j]=MAXN;
        m=n*(n-1)/2;

        while(m--)
        {
            scanf("%d %d %d",&x,&y,&s);

            map[x][y]=map[y][x]=s;
        }
        sum=prime(n);
        printf("%d\n",sum);

    }
    return 0;
}

 

法二:kruskal算法

加邊法。在這裏我使用了並查集,來判斷新加入的邊的兩點是否已經連通。

對所有的邊進行排序,從小到大 的選擇邊,判斷即將加入的邊的兩點是否已經連通,是則不加入,否則加入。

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 10000

typedef struct _node{
    int val;
    int start;
    int end;
}Node;

Node V[N];
int f[N];

void init(int n)
{
    int i;
    for(i=1;i<=n;i++)
       f[i]=i;
}
int find(int x)
{
    while(x!=f[x])
        x=f[x];
    return x;
}
void merge(int x,int y)
{
    int xx=find(x);
    int yy=find(y);
    if(x!=y)
        f[xx]=yy;
}

int kruskal(int n)
{
    int i,x,y,s=0;
        int m=n*(n-1)/2;
    init(n);
    for(i=0;i<m;i++)
    {
        x=V[i].start;
        y=V[i].end;
        if(find(x)!=find(y))
        {
            merge(x,y);
            s+=V[i].val;
        }

    }
    return s;
}
void initv(int m)
{
    int i,x,y,s;
    for(i=0;i<m;i++)
    {
        scanf("%d %d %d",&x,&y,&s);
        V[i].start=x;
        V[i].end=y;
        V[i].val=s;
    }

}

int cmp(const void *a, const void *b)
{
   return(*(Node *)a).val - (*(Node*)b).val;
}

int main()
{
    int n,i,j,x,y,s,m,sum=0;
    while(scanf("%d",&n))
    {
        if(n==0) break;
        int m=n*(n-1)/2;
        initv(m);
        qsort(V,m,sizeof(V[0]), cmp);
        sum=kruskal(n);

        printf("%d\n",sum);

    }
    return 0;
}

 

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