题目描述
"村村通"是国家一个系统工程,其包涵有:公路、电力、生活和饮用水、电话网、有线电视网、互联网等等。
村村通公路工程,是国家为构建和谐社会,支持新农村建设的一项重大举措,是一项民心工程。又称“五年千亿元”工程
该工程是指中国力争在5年时间实现所有村庄通沥青路或水泥路,以打破农村经济发展的交通瓶颈,解决9亿农民的出行难题。
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
要求用Kruskal算法求解
输入
第1行:顶点数n
第2行:n个顶点编号
第3行:边数m
接着m行:m条边信息,格式为:顶点1 顶点2 权值
输出
第1行:输出最小生成树的权值之和
接着n-1行对应n-1条边信息
如果能找到最小生成树,按树的生长顺序输出, 边顶点按数组序号升序输出
如果输入数据不足以保证畅通,则直接输出−1,无需输出任何边信息
输入样例1
6
v1 v2 v3 v4 v5 v6
10
v1 v2 6
v1 v3 1
v1 v4 5
v2 v3 5
v2 v5 3
v3 v4 5
v3 v5 6
v3 v6 4
v4 v6 2
v5 v6 6
输出样例1
15
v1 v3 1
v4 v6 2
v2 v5 3
v3 v6 4
v2 v3 5
NOTICE:
克鲁斯卡尔算法(Kruskal)主要有以下几点需要注意:(1)Kruskal算法是以边为操作对象,因此需定义类Edge,图的私有属性中,用edge数组代替邻接矩阵(2)所需变量有weightsum,为最小生成树的权值之和;(3)算法分两步,一是对边进行排序,二是选边;选边从小到大挑选(这也是排序的原因),并且选出来的边不构成闭环;那如何判断选的边是否会构成闭环呢:这里采用标号法,将所有顶点的flag初始化为其下标,每选择一条边,便将边两端的顶点修改为一致;因此,选边时不构成闭环的条件就是这条边两端的顶点的flag不相等;这个过程有很多细节需要注意,具体见代码注释;
#include <iostream>
#include <algorithm>
using namespace std;
class Edge
{
public:
string start, end;
int weight;
};
bool cmp(Edge e1, Edge e2)
{
if (e1.weight < e2.weight)
return true;
return false;
}
class Graph
{
private:
string* data;
Edge* edge;
int vertexnum;
int edgenum;
int* flag;//顶点标号,判断是否构成闭环
int find(string s)//根据顶点信息,返回顶点下标
{
for (int i = 0; i < vertexnum; i++)
if (s == data[i])
return i;
}
public:
Graph()
{
cin >> vertexnum;
data = new string[vertexnum];
for (int i = 0; i < vertexnum; i++)
cin >> data[i];
cin >> edgenum;
edge = new Edge[edgenum];
for (int i = 0; i < edgenum; i++)
cin >> edge[i].start >> edge[i].end >> edge[i].weight;
flag = new int[vertexnum];
for (int i = 0; i < vertexnum; i++)//全都初始化为自己的下标
flag[i] = i;
}
~Graph()
{
delete[]data;
delete[]edge;
delete[]flag;
}
void Kruskal()
{
Edge* result = new Edge[vertexnum - 1];//存储结果,方便输出
int index = 0;//result的下标
int weight_sum = 0;
//升序排序
sort(edge, edge + edgenum, cmp);
//选边,只需要n-1条边
for (int i = 0; i < edgenum && index != vertexnum-1; i++)
{
if (flag[find(edge[i].start)] != flag[find(edge[i].end)])//这条边的start的flag 不等于 这条边的end的flag
{
result[index++] = edge[i];
weight_sum += edge[i].weight;
}
//把这条边的两个顶点的flag改为一致(哪个赋给哪个都可以)
//有时候是两个分量的连接,这时候改的就不止一个顶点,是其中一个分量的所有顶点,都改为与另一个分量一致
//这里统一将end改为与start一致
int tmp = flag[find(edge[i].end)];//一定要用一个临时变量记录,因为原来的end的flag会被改为与start一致
for (int j = 0; j < vertexnum; j++)
{
if (flag[j] == tmp)
flag[j] = flag[find(edge[i].start)];
}
}
//不连通的输出
for (int i = 0; i < vertexnum - 1; i++)
{
if (flag[i] != flag[i + 1])
{
cout << "-1" << endl;
return;
}
}
//输出
cout << weight_sum << endl;
for (int i = 0; i < vertexnum - 1; i++)
{
//题目要求边顶点按数组序号升序输出(破题要求真多)
if (find(result[i].start) < find(result[i].end))
cout << result[i].start << " " << result[i].end << " " << result[i].weight << endl;
else
cout << result[i].end << " " << result[i].start << " " << result[i].weight << endl;
}
}
};
int main()
{
Graph g;
g.Kruskal();
return 0;
}