题目背景
农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。
题目描述
约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。
你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过100000
这道题是最小生成树的模板题。
Prim解法
Prim算法是通过每次添加一个新节点加入集合,直到所有点加入停止的 最小生成树的算法。
时间复杂度:O(n²)
这里没有使用堆进行优化,感兴趣的话看一看最小生成树算法,里面有使用堆优化的代码。
#include<iostream>
#include<cmath>
#include<string.h>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
#define Max 10005
#define ll int
#define inf 10000000
#define p pair<ll,ll>
ll n, dis[Max][Max], i, j, ans = 0, mincost[Max];
bool used[Max];
void Prim() {
//以1为起点
for (i = 1; i <= n; i++) used[i] = false;
ans = 0; for (i = 2; i <= n; i++) mincost[i] = inf; mincost[1] = 0; ll cnt = 0;
while (cnt < n) {
ll m = inf, id = -1;
for (i = 1; i <= n; i++)
if (!used[i] && mincost[i] < m) {
m = mincost[i]; id = i;//从未使用过得点中找一个距离最小的
}
if (id == -1)break;//不存在距离比较短的边
used[id] = true; ans += m; cnt++;
for (i = 1; i <= n; i++) mincost[i] = min(mincost[i], dis[id][i]);
}
}
int main() {
while (cin >> n) {
for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) cin >> dis[i][j];
Prim();
cout << ans << endl;
}
}
Kruskal解法
Kruskal算法是通过并查集,按照边的权重顺序(从小到大)将边加入生成树中,但是若加入该边会与生成树形成环则不加入该边,选其次。直到树中含有n - 1条边为止。
时间复杂度:O(E log E)(E为边数)
ll find(ll s) {
if (s == kind[s]) return s;
return kind[s] = find(kind[s]);
}
void unite(ll a, ll b) {
ll xx = find(a), yy = find(b);
kind[yy] = xx;
}
void Kruskal() {
for (i = 0; i <= n; i++) kind[i] = i; ll sum = 0;
sort(e + 1, e + cnt); i = 1;//根据边权进行排序
while (i < cnt) {
edge tmp = e[i++];
if (find(tmp.from) != find(tmp.to)) {
sum++;
unite(tmp.from, tmp.to);
ans += tmp.weight;
}
if (sum == n - 1) break;
}
}
完整代码
#include<iostream>
#include<cmath>
#include<string.h>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
#define Max 10005
#define ll int
#define inf 10000000
#define p pair<ll,ll>
struct edge {
ll from, to, weight = inf;
bool operator<(edge e) {
return weight < e.weight;
}
}e[Max];
ll n, dis[Max][Max], i, j, ans = 0, cnt = 1, kind[Max];
ll find(ll s) {
if (s == kind[s]) return s;
return kind[s] = find(kind[s]);
}
void unite(ll a, ll b) {
ll xx = find(a), yy = find(b);
kind[yy] = xx;
}
void Kruskal() {
for (i = 0; i <= n; i++) kind[i] = i; ll sum = 0;
sort(e + 1, e + cnt); i = 1;//根据边权进行排序
while (i < cnt) {
edge tmp = e[i++];
if (find(tmp.from) != find(tmp.to)) {
sum++;
unite(tmp.from, tmp.to);
ans += tmp.weight;
}
if (sum == n - 1) break;
}
}
int main() {
while (cin >> n) {
ans = 0, cnt = 1;
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++) {
if (j > i) {
e[cnt].from = i; e[cnt].to = j, e[cnt++].weight = dis[i][j];
}
}
Kruskal();
cout << ans << endl;
}
}