问题:畅通工程(06年浙江大学研究生上机题目)
问题描述:
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
输入:
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
输出:
对每个测试用例,在1行里输出最小的公路总长度。
样例输入:
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
样例输出:
3
5
kruskal算法思想:
先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。(摘自百度百科~~)
并查集简介:
并查集就是树的双亲存储结构,可以简单的用一个数组表示,数组下表表示结点的编号,数组值存储该结点双亲结点的编号,并且可以把根结点的双亲设置为-1等特殊值,以方便操作。
int vset[N]; //定义一个并查集
int findRoot(int x) //在并查集中找到根结点,并进行路径压缩
{
if(vset[x]==-1) return x;
else
{
int root = findRoot(vset[x]);
vset[x]=root;
return root;
}
}
for (int i = 1; i < N; i++) //初始化并查集
vset[i]=-1;
初始化并查集,开始认为每个结点均自己构成一个集合,不和其他结点为伍~~~
问题分析:
简单的说就是,将各边按权值进行递增排序,然后由小到大选出还未在最小生成树中的边,将边的权值累加,直至所有结点都加入最小生成树(怎样防止将边或者结点重复加入生成树,这里就用到并查集)
看代码吧:
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 4951
struct Road //边的结构体
{
int a,b;
int weight;
bool operator < (const Road & A) const{ //重载小于运算符,用于排序
return weight < A.weight;
}
};
int vset[N]; //定义一个并查集
int findRoot(int x) //在并查集中找到根结点,并进行路径压缩
{
if(vset[x]==-1) return x;
else
{
int root = findRoot(vset[x]);
vset[x]=root;
return root;
}
}
int main()
{
int n,a,b,w;
Road road[N];
int root_1,root_2;
int roadLength;
int tmp;
while (scanf("%d",&n)!=EOF&& n!=0)
{
tmp=n*(n-1)/2;
/*一下是kruskal算法精髓*/
for (int i = 1; i < N; i++) //初始化并查集
vset[i]=-1;
for (int i = 1; i <=tmp; i++) //输入边两头结点编号及边权值
{
scanf("%d%d%d",&a,&b,&w);
road[i].a=a;road[i].b=b;road[i].weight=w;
}
sort(road+1,road+tmp+1); //对边按权值又小到大进行排序,注意这里使用了STL
roadLength=0;
for (int i = 1; i <= tmp; i++)
{
root_1 = findRoot(road[i].a);
root_2 = findRoot(road[i].b);
if(root_1!=root_2)
{
vset[root_2]=root_1;
roadLength+=road[i].weight; //累加最小公路长度
}
}
printf("%d\n",roadLength); //输出
}
return 0;
}
黑框运行结果:
后记:
本例还用到了c++的STL中的sort函数,用法为sort(起点地址,结束地址);且sort默认递增排序