【ACM】- HDU-1879 繼續暢通工程 【最小生成樹】
阿新 • • 發佈:2018-12-09
題目連結
題目分析
最小生成樹問題;
解題思路
用Kruskal
演算法即可,把修通道路(邊)的權值(距離)令為0
即可;
AC程式(C++)
/**********************************
*@ID: 3stone
*@ACM: HDU-1879 繼續暢通工程
*@Time: 18/9/13
*@IDE: VSCode + clang++
***********************************/
#include<cstdio >
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 110;
struct edge{
int u, v, cost;
edge() {}
edge(int _u, int _v, int _cost) : u(_u), v(_v), cost(_cost) {} //建構函式,便於加入結點
bool operator < (const edge& n) const { //規定優先順序
return cost > n.cost; //注意和sort函式是相反的
}
};
int N, M;
int far[maxn]; //並查集
//尋根
int find_root(int a) {
int root = a;
while(root != far[root]) root = far[root];
while(a != far[a]) { //路徑壓縮
int cur = a;
a = far[a];
far[cur] = root;
}
return root;
}
//合併集合
void union_set(int a, int b) {
int root_a = find_root(a);
int root_b = find_root(b);
if (a != b){
far[root_b] = root_a;
}
}
int kruskal(priority_queue<edge> E) {
for(int i = 1; i <= N; i++) far[i] = i; //初始化並查集
int ans = 0;//權值和
int edge_num = 0; //已選擇的邊數
int cnt = N; //連通塊數
for(int i = 0; i < M; i++) {
edge e = E.top(); E.pop(); //get fisrt edge
int root_u = find_root(e.u);
int root_v = find_root(e.v);
if(root_u != root_v) { //if not belong to same set
union_set(root_u, root_v);
edge_num++;
cnt--; //連通塊數-1
ans += e.cost;
}
if(edge_num == N - 1) break; //邊數等於結點數-1
}
if(cnt != 1) return -1;//只剩一個連通塊(edge_num != N - 1 也沒問題)
else return ans;
}//kruskal
int main() {
int a, b, cost, status;
while(scanf("%d", &N) != EOF) {
if(N == 0) break;
M = N * (N - 1) / 2;
priority_queue<edge> E; //儲存所有邊(無clear()函式,每次重新定義時間最快)
//優先順序和sort()函式是相反的
for(int i = 0; i < M; i++) {
scanf("%d %d %d %d", &a, &b, &cost, &status);
if(status == 0) { //未修通
E.push(edge(a, b, cost)); //加入堆
} else { //已修通
E.push(edge(a, b, 0)); //已修通,權值令為0
}
}
printf("%d\n", kruskal(E)); //輸出最小成本
}//while
system("pause");
return 0;
}