现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数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算法+按秩优化和压缩路径的并查集.
上代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class UnionFind {//并查集
public:
int* root;
int* rank;
int count;//用于判断数据是否足够,可以使图连通
UnionFind(int size)//初始化
{
count=size;
root = new int[size];
rank = new int[size];
for (int i = 0;i < size;i++)
{
root[i] = i;
rank[i] = 1;
}
}
int find(int x)//路径压缩
{
return root[x] == x ? x : root[x] = find(root[x]);
}
void Union(int x, int y)//按秩优化
{
count--;//每合并一次就少一个结点,如果村村连通,则应该变成1
int rootx = find(x), rooty = find(y);
if (rootx != rooty)
{
if (rank[rootx] > rank[rooty])
{
root[rooty] = rootx;
}
else if (rank[rootx] < rank[rooty])
{
root[rootx] = rooty;
}
else
{
root[rootx] = rooty;
rank[rooty]++;
}
}
}
int isConnected(int x, int y)
{
return find(x) == find(y);
}
};
int main()
{
int n, m;
int res = 0, cnt = 0;
cin >> n >> m;
vector<vector<int>> graph;
for (int i = 0;i < m;i++)
{
vector<int> tmp;
tmp.resize(3);
cin >> tmp[0] >> tmp[1] >> tmp[2];
tmp[0]--;tmp[1]--;//因为题目给的结点编号从1开始,为了对应数组的下标号,减去1
graph.push_back(tmp);
}//初始化将村落的数据导入进graph
sort(begin(graph), end(graph), [](const auto& a, const auto& b)
{
return a.back() < b.back();
});//将graph按权重从小到大排序
UnionFind uf(n);
for (const auto& it : graph)
{
if (cnt == n - 1) break;//最小生成树最多只可能有n-1条边
if (uf.isConnected(it[0], it[1])) continue;//判断是否成环
else
{
uf.Union(it[0], it[1]);
res += it[2];
cnt++;//记录最小生成树的边数
}
}
if(uf.count!=1)//判断数据是否足够使得村落互通(即图是否连通)
{
cout<<-1;
return 0;
}
cout << res;
}