数据结构》08-图7 公路村村通(最小生成树问)
题目
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数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
—————————————————————————
方法一:Kruskal算法
核心–不停地找权重最小的变合在一起但需要注意的是 不能构成回路 如何找最小权重所在的边,可以靠最小堆来解决,如何判断是否构成回路呢,可以用并查集看是否属于同一棵树,这可以参考之前的并查集的blog
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define maxsize 1001
#define inf 99999999999
int G[maxsize][maxsize];
int d[maxsize];
int parent[maxsize];
vector<int>v;
int n, m, a1, a2;
struct edge
{
int u, v;
int cost;
}e[maxsize];
bool cmp(edge a, edge b)
{
return a.cost < b.cost;
}
int father[maxsize];
int findfather(int i)
{
int a = i;
while (father[i] != i)
{
i = father[i];
}
while (father[a] != a)
{
int z = a;
a = father[a];
father[a] = i;
}
return i;
}
int Krus(int n, int m)
{
int ans = 0, num_edge = 0;
for (int i = 0; i < n; i++)
{
father[i] = i;
}
sort(e, e + m, cmp);
for (int i = 0; i < m; i++)
{
int fau = findfather(e[i].u);
int fav = findfather(e[i].v);
if (fau != fav)
{
father[fau] = fav;
ans += e[i].cost;
num_edge++;
if (num_edge == n - 1) break;
}
}
if (num_edge != n - 1) return -1;
else return ans;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < m; i++)
{
cin >> e[i].u >> e[i].v >> e[i].cost;
}
int ans = Krus(n, m);
cout << ans << endl;
}
—————————————————————————
方法二
prim算法,类似于DJ算法,贪心思想,找到临近最短的就更新
#include<iostream>
#include<vector>
using namespace std;
#define inf 100000
#define maxsize 1001
int g[maxsize][maxsize];
int parent[maxsize];
int dist[maxsize];
int n;
int m;
int sum;
using namespace std;
vector<int>mst;
void build()
{
int v1, v2;
int w;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
g[i][j] = 0;
}
dist[i]= inf;
parent[i] = -1;
}
for (int i = 0; i < m; i++) {
cin >> v1 >> v2 >> w;
g[v1][v2] = w;
g[v2][v1] = w;
}
}
void init(int x) //初始化第一个点和到周围点的距离
{
dist[x] = 0;
mst.push_back(x);
for (int i = 1; i <= n; i++) {
if (g[x][i]) {
dist[i] = g[x][i];
parent[i] = x;
}
}
}
int findmin() //找点周围路径最短的点
{
int min = inf;
int xb = -1;
for (int i = 1; i <= n; i++) {
if (dist[i] && dist[i] < min) {
min = dist[i];
xb = i;
}
}
return xb;
}
void prim(int s)
{
init(s);
while (1) {
int v = findmin();
if (v == -1) //不存在这样的点
break;
sum += dist[v];
dist[v] = 0;
mst.push_back(v);
for (int i = 1; i <= n; i++) {
if (g[v][i] && g[v][i] < dist[i]) {
dist[i] = g[v][i];
parent[i] = v;
}
}
}
}
int main()
{
build();
prim(1); //哪个点开始都无所谓的
if (mst.size() == n)
cout << sum;
else
cout << -1;
return 0;
}
#include<iostream>
#include<vector>
using namespace std;
#define maxsize 1001
#define inf 99999999999
int G[maxsize][maxsize];
int d[maxsize];
int parent[maxsize];
vector<int>v;
int n, m, a1, a2;
int findmin()
{
int min = inf;
int xb = -1;
for (int i = 1; i <= n; i++)
{
if (d[i]&&d[i] < min)
{
min = d[i];
xb = i;
}
}
return xb;
}
int main()
{
cin >> n >> m;
int w;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
G[i][j] = 0;
}
d[i] = inf;
}
for (int i = 0; i < m; i++)
{
cin >> a1 >> a2 >> w;
G[a2][a1] = G[a1][a2] = w;
}
d[1] = 0;
v.push_back(1);
for (int i = 1; i <= n; i++)
{
if (G[1][i])
{
d[i] = G[1][i];
}
}
int sum = 0;
while (1)
{
int q = findmin();
if (q == -1)
break;
sum += d[q];
d[q] = 0;
v.push_back(q);
for (int i = 1; i <= n; i++)
{
if (G[q][i]&&G[q][i] < d[i])
{
d[i] = G[q][i];
}
}
}
if (v.size() == n)
cout << sum << endl;
else
cout << "-1" << endl;
}