算法之最小生成樹(繼續暢通工程)
阿新 • • 發佈:2017-09-10
沒有 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; }
算法之最小生成樹(繼續暢通工程)