#100-【最小生成森林(最小生成樹變種)】灌水
阿新 • • 發佈:2018-12-10
#100祭!
Description
Farmer John已經決定把水灌到他的n(1<=n<=300)塊農田,農田被數字1到n標記。把一塊土地進行灌水有兩種方法,從其他農田飲水,或者這塊土地建造水庫。 建造一個水庫需要花費wi(1<=wi<=100000),連線兩塊土地需要花費Pij(1<=pij<=100000,pij=pji,pii=0). 計算Farmer John所需的最少代價。
Input
第一行:一個數n
第二行到第n+1行:第i+1行含有一個數wi
第n+2行到第2n+1行:第n+1+i行有n個被空格分開的數,第j個數代表pij。
Output
第一行:一個單獨的數代表最小代價.
Sample Input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Sample Output
9
HINT
【輸出詳解】 Farmer John在第四塊土地上建立水庫,然後把其他的都連向那一個,這樣就要花費3+2+2+2=9
先是0(超級源點)到i連邊長度wi,隨後正常建圖即可。
#include <iostream> #include <vector> #include <algorithm> #define SIZE 310 using namespace std; struct edge { int from, to, dis; }; vector<edge> graph; int pre[SIZE]; bool comp(edge a, edge b) { return a.dis < b.dis; } int find(int x) // 找祖先(並查集操作) { return (pre[x]) ? pre[x] = find(pre[x]) : x; // 順便狀態壓縮 } int main(int argc, char** argv) { int n, i, j, x, res = 0, c = 0, u, v, w, prex, prey; scanf("%d", &n); for (i = 1; i <= n; ++i) { scanf("%d", &x); graph.push_back({0, i, x}); // 連0點(最小生成森林建圖) } for (i = 1; i <= n; ++i) // 正常建圖 { for (j = 1; j <= n; ++j) { scanf("%d", &x); if (i <= j) { graph.push_back({i, j, x}); } } } sort(graph.begin(), graph.end(), comp); // 注意vector型別要用vector::begin()和vector::end()函式作為開頭和結尾 for (i = 0; i < graph.size(); ++i) // K演算法求最小生成樹 { u = graph[i].from; v = graph[i].to; w = graph[i].dis; prex = find(u); // 看建這條邊是否有必要(並查集操作) prey = find(v); if (prex ^ prey) // ^是位運算異或符,這裡是不等於(!=)的意思 { res += w; if (++c == n) // 連了n(= n + 1 - 1)條邊,直接退出 { break; } pre[prex] = prey; } } printf("%d", res); return 0; }