7-10 公路村村通 (30分)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
题解:
一道MST裸题,刚好可以复习一下Prim和Kruskal。
Prim和Dijkstra的贪心+动规思想差不多,连代码实现都很像==
Krusal代码过程就更好理解了,并查集抬手就来。
Version 1 Prim朴素版本
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
const int inf = 0x3f3f3f3f;
bool vis[maxn];
int n, m, dis[maxn], g[maxn][maxn];
inline const int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }
return x * f;
}
int prim()
{
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
for (int i = 0; i < n - 1; i++)
{
int u = 0, mdis = inf;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && mdis > dis[j])
{
mdis = dis[j];
u = j;
}
}
if (u == 0) return -1;
vis[u] = true;
for (int v = 1; v <= n; v++)
if (g[u][v] != inf && !vis[v])
dis[v] = min(dis[v], g[u][v]);
}
int res = 0;
for (int i = 1; i <= n; i++) res += dis[i];
return res;
}
int main()
{
n = read(); m = read();
memset(g, 0x3f, sizeof g);
for (int i = 1; i <= n; i++) g[i][i] = 0;
while (m--)
{
int u = read(), v = read(), w = read();
g[u][v] = g[v][u] = w;
}
printf("%d\n", prim());
return 0;
}
Version 2 Prim堆优化版本
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> p;
const int maxn = 1e3 + 10;
const int inf = 0x3f3f3f3f;
bool vis[maxn];
int n, m, g[maxn][maxn];
inline const int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }
return x * f;
}
int prim()
{
priority_queue<p, vector<p>, greater<p> > q;
q.push(p{ 0, 1 });
int cnt = 0, sum = 0;
while (!q.empty() && cnt <= n)
{
int d = q.top().first;
int u = q.top().second;
q.pop();
if (vis[u]) continue;
vis[u] = true;
cnt++;
sum += d;
for (int v = 1; v <= n; v++)
if (!vis[v] && g[u][v] != inf)
q.push(p{ g[u][v], v });
}
if (cnt < n) return -1;
return sum;
}
int main()
{
n = read(); m = read();
memset(g, 0x3f, sizeof g);
for (int i = 1; i <= n; i++) g[i][i] = 0;
while (m--)
{
int u = read(), v = read(), w = read();
g[u][v] = g[v][u] = w;
}
printf("%d\n", prim());
return 0;
}
Version 3 Kruskal
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
int n, m, pre[maxn];
struct edge
{
int u, v, w;
bool operator < (const edge& n) const { return w < n.w; }
};
vector<edge> e;
inline const int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }
return x * f;
}
int Find(int id)
{
return pre[id] == id ? id : pre[id] = Find(pre[id]);
}
void Union(int a, int b)
{
pre[Find(b)] = Find(a);
}
int kruskal()
{
int cnt = 0, sum = 0;
for (int i = 1; i <= n; i++) pre[i] = i;
sort(e.begin(), e.end());
for (int i = 0; i < (int)e.size() && cnt <= n - 1; i++)
{
int u = e[i].u, v = e[i].v, w = e[i].w;
if (Find(u) != Find(v))
{
Union(u, v);
sum += w;
cnt++;
}
}
return cnt == n - 1 ? sum : -1;
}
int main()
{
n = read(); m = read();
while (m--)
{
int u = read(), v = read(), w = read();
e.push_back(edge{ u, v, w });
}
printf("%d\n", kruskal());
return 0;
}