1. 程式人生 > >算法之最小生成樹(繼續暢通工程)

算法之最小生成樹(繼續暢通工程)

沒有 roo space als pri () 最短 con include

個人比較愛好刷算法題,然後最近遇到一個算法題,是最小生成樹的問題,是繼續暢通工程,首先先看下具體要求:

省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可)。現得到城鎮道路統計表,表中列出了任意兩城鎮間修建道路的費用,以及該道路是否已經修通的狀態。現請你編寫程序,計算出全省暢通需要的最低成本。

輸入描述:

    測試輸入包含若幹測試用例。每個測試用例的第1行給出村莊數目N ( 1< N < 100 );隨後的 N(N-1)/2 行對應村莊間道路的成本及修建狀態,每行給4個正整數,分別是兩個村莊的編號(從1編號到N),此兩村莊間道路的成本,以及修建狀態:1表示已建,0表示未建。

當N為0時輸入結束。

輸出描述:

    每個測試用例的輸出占一行,輸出全省暢通需要的最低成本。
示例1

輸入

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

輸出

3
1
0
然後分析,這個題的實際就是最小生成樹的問題,可以采用prim算法和kruskal算法,然後最後計算生成樹的邊數的總和,則就是要的結果,但是這個題比較巧妙的是又添加了一個附加條件,則是有的路已經修了,所以這個需要在輸入的時候做一點點小小的改動,那就是對於已經修好的路,我們需要將其邊的權重設置為0,這樣,則不需要在原算法上做任何修改,則可以實現。
使用的prim算法(具體的prim算法的思路這裏不再闡述了)
#include <stdio.h>

using namespace std;
//定義這個圖的存儲方式,個人比較喜歡用矩陣的方式,也可以使用鄰接表的方法
int tree[100][100];
//記錄點遍歷的信息 bool isFind[100]; int main() { int n; while(scanf("%d",&n)!=EOF && n!=0) {
     //初始化數據 for(int i = 0;i<n;i++) { for(int j = 0; j<n;j++) tree[i][j] = -1; isFind[i] = false; }
     //輸入數據,做一個小操作,如果輸入為1,則權重為0 for(int i =0 ; i< n*(n-1)/2;i++) { int a,b,c,d; scanf("%d %d %d %d",&a,&b,&c,&d); if(d == 1) { c = 0; } tree[a-1][b-1] = c; tree[b-1][a-1] = c; }
     //用來統計路徑 int countDistance = 0; while(true) { int minDistance = 10000; int minNode = 0;
//找尋當前未遍歷的點中最短的路徑 for(int i = 1 ;i<n;i++) { if(!isFind[i]&&minDistance>tree[0][i]) { minDistance = tree[0][i]; minNode = i; } }
       //更新相關數據,遍歷信息和總長度信息 isFind[minNode] = true; countDistance += minDistance; int isResult = true;
       //判斷是否已經遍歷完所有的點,是則退出,否則繼續循環 for(int i = 1 ;i<n;i++) { if(!isFind[i]) isResult = false; } if(isResult) break;
//如果沒有遍歷完,更新當前樹與未遍歷的點的信息 for(int i = 1;i<n;i++) { if(!isFind[i]) { if(tree[minNode][i]<tree[0][i]) tree[0][i] = tree[minNode][i]; } } } printf("%d\n",countDistance); } return 0; }

下面則是kruskal算法的實現:

#include<stdio.h>
#include<algorithm>

using namespace std;
#define N 101
int Tree[N];

struct Edge {
    int a, b;//邊兩個頂點的編號
    int cost;//該邊的權值

    bool operator<(const Edge &A) const {//重載小於號使其可以按照邊權從小到大排序
        return cost < A.cost;
    }
} edge[6000];

int findRoot(int x) {//查找代表集合的樹的根節點
    if (Tree[x] == -1) {
        return x;
    } else {
        int temp = findRoot(Tree[x]);
        Tree[x] = temp;
        return temp;
    }
}

int main() {
    int n;
    while (scanf("%d", &n) != EOF && n != 0) {
        int m = n * (n - 1) / 2;
        int d;
        for (int i = 1; i <= m; i++) {
            scanf("%d %d %d %d", &edge[i].a,
                  &edge[i].b, &edge[i].cost,&d);
            if(d == 1)
                edge[i].cost = 0;
        }
        sort(edge + 1, edge + m + 1);
        for (int i = 1; i <= N; i++) {
            Tree[i] = -1;
        }
        int count = 0;//最小生成樹上邊權的和,初始值為0
        for (int i = 1; i <= m; i++) {//按照邊權值遞增排序遍歷所有的邊
            int aRoot = findRoot(edge[i].a);
            int bRoot = findRoot(edge[i].b);//查找兩個頂點的集合信息
            if (aRoot != bRoot) {
                Tree[aRoot] = bRoot;//合並兩個集合
                count += edge[i].cost;//累加該邊權值
            }
        }
        printf("%d\n", count);//輸出
    }

    return 0;
}

  

  



算法之最小生成樹(繼續暢通工程)