您的位置:首页 > 其它

还是畅通工程 - 两种经典最小生成树算法的实战

2017-05-29 16:16 295 查看

Problem Description

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。 当N为0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最小的公路总长度。

Sample Input

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

Sample Output

3

5

Source

浙大计算机研究生复试上机考试-2006年

题解

由题意知,求最小生成树。有两种算法,Prim和Kruskal

方法一:(Kruskal算法)

Exe.Time:280 MS

Exe.Memory:1752 KB

算法:加边法。先给边按权值排序,权值小优先选择原则,逐步增加生成树的边。

每次选择一条路径,判断该路径的两点是否已连通(建标记数组flag[500]={0},判断两点是否连通的条件是两点flag的都为1,若为两点未连通,给两点都标记为1视已连通,并求和路径),一直到所有点都连通为止(即所有点都在一个集合内为止)。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
struct node
{
int x,y;
int len;
}nod[5000];
//N<100,n最大为100*99/2约等于5000;
bool cmp(node x,node y)
{
return x.len<y.len;
}
int main()
{
int N;
while(~scanf("%d",&N)&&N){
int ans=0;
int n=N*(N-1)/2;
for(int i=0;i<n;i++){//输入两点及两点路径。
scanf("%d%d%d",&nod[i].x,&nod[i].y,&nod[i].len);
}
sort(nod,nod+n,cmp);//给两点间的路径排序。优先选择最小。
bool flag[105]={0};
flag[nod[0].x]=1;//默认先选择od[0].x为起始点。
int NN=1;//已连通的点数
for(int i=0;i<n;i++)
{
int num=flag[nod[i].x]+flag[nod[i].y];
if(NN==N) break;//若所有点都连通则退出。
if(num==1){
ans+=nod[i].len;
NN++;
flag[nod[i].x]=flag[nod[i].y]=1;
i=0;//重新遍历未连通的最小路径的点。
}
}
printf("%d\n",ans);
}
return 0;
}


方法二:(Prim算法)

Exe.Time:156 MS

Exe.Memory:1728 KB

算法:“加点法”。以v0为起点,扫描所有点与v0的权值存入dis数组中。

循环n-1次(n为点数,n-1次循环即为添加n-1条边)。每次循环,找到dis数组中最小权值的点,标记到flag数组中为1,表示已连接,然后扫描所有未连接的点与k点权值,更新dis数组(因为,v0与k已经连接,而要找最小生成边,从v0到u的最短距离就更新为k到u的最短距离。)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define INF 1000000
int map[110][110];
int prim(int n)
{
int used[110],dis[110],i,j,k,min,sum=0;
//flag记录该点是否被标记,lowc为当前图中已连接点与未连接点的最小权值
memset(used,0,sizeof(used));
memset(dis,0,sizeof(dis));
for(i=1;i<=n;i++){
dis[i]=map[i][1];
}
for(i=1;i<n;i++){//n-1循环,即n个点之间加n-1条边
min=INF;
for(j=2;j<=n;j++){
if(min>dis[j] && used[j]==0){
min=dis[j];
k=j;
}
}
sum+=min;
used[k]=1;//标记K点
for(j=1;j<=n;j++){
//扫描未连接点j与已连接点集合中的最短距离,更新入dis
if(used[j]==0 && dis[j]>map[k][j])
dis[j]=map[k][j];
}
}
return sum;
}
int main(){
int n,m,i,j,a,b,c;
while(~scanf("%d",&n)&& n)
{
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j)
map[i][j]=0;
else
map[i][j]=INF;
}
}//和memset(map,0,sizeof(map));类似
m=n*(n-1)/2;
for(i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&c);
map[a]=map[b][a]=c;
}
printf("%d\n",prim(n));
}
return 0;
}


[b]**运算时间总结:



上下两种声明方式,下图的声明方式相较于上图声明方式运行速度快。





上下两种声明方式,下图的声明方式相较于上图声明方式运行速度快。



相关链接

畅通工程 - 并查集
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息