问题描述
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
输入
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
输出
对每个测试用例,在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
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
连通并且最短距离,显然是最小生成树问题。
最小生成树问题有两种基本解决算法:
(1)prim算法(加点法)
(2)kruskal算法(加边法)
法一:prim算法
加点法。
初始化点1,设置为已访问visit[1]=1。将每个未访问点到访问点的最短距离初始化为到点1 的距离,dis[j]=map[1][j]。
不断选择未访问到访问点的最短距离dis[n],加入最短距离,将最短距离的点设置为已访问。
重新计算每个未访问点的最短距离。
对n-1个点进行选择。(n-1次循环)
#include <stdio.h>
#include <string.h>
#define MAXN 100000
#define N 110
int map[N][N];
int visit[N];
int dis[N];
int prime(int n)
{
int i,j,min,k,s=0;
memset(visit,0,sizeof(visit));
for(i=1;i<=n;i++)
dis[i]=map[1][i];
visit[1]=1;
for(i=1;i<n;i++) //n-1个点
{
min=MAXN;
for(j=1;j<=n;j++)
{
if(visit[j]==0&&dis[j]<min) //从1出发选择一条最短的路
{
min=dis[j];
k=j;
}
}
visit[k]=1; //k也访问过了,更新所有未访问过的最短距离
s=s+dis[k];
for(j=1;j<=n;j++)
{
if(visit[j]==0&&dis[j]>map[k][j])
{
dis[j]=map[k][j];
}
}
}
return s;
}
int main()
{
int n,i,j,x,y,s,m,sum=0;
while(scanf("%d",&n))
{
if(n==0) break;
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
map[i][j]=MAXN;
m=n*(n-1)/2;
while(m--)
{
scanf("%d %d %d",&x,&y,&s);
map[x][y]=map[y][x]=s;
}
sum=prime(n);
printf("%d\n",sum);
}
return 0;
}
法二:kruskal算法
加边法。在这里我使用了并查集,来判断新加入的边的两点是否已经连通。
对所有的边进行排序,从小到大 的选择边,判断即将加入的边的两点是否已经连通,是则不加入,否则加入。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 10000
typedef struct _node{
int val;
int start;
int end;
}Node;
Node V[N];
int f[N];
void init(int n)
{
int i;
for(i=1;i<=n;i++)
f[i]=i;
}
int find(int x)
{
while(x!=f[x])
x=f[x];
return x;
}
void merge(int x,int y)
{
int xx=find(x);
int yy=find(y);
if(x!=y)
f[xx]=yy;
}
int kruskal(int n)
{
int i,x,y,s=0;
int m=n*(n-1)/2;
init(n);
for(i=0;i<m;i++)
{
x=V[i].start;
y=V[i].end;
if(find(x)!=find(y))
{
merge(x,y);
s+=V[i].val;
}
}
return s;
}
void initv(int m)
{
int i,x,y,s;
for(i=0;i<m;i++)
{
scanf("%d %d %d",&x,&y,&s);
V[i].start=x;
V[i].end=y;
V[i].val=s;
}
}
int cmp(const void *a, const void *b)
{
return(*(Node *)a).val - (*(Node*)b).val;
}
int main()
{
int n,i,j,x,y,s,m,sum=0;
while(scanf("%d",&n))
{
if(n==0) break;
int m=n*(n-1)/2;
initv(m);
qsort(V,m,sizeof(V[0]), cmp);
sum=kruskal(n);
printf("%d\n",sum);
}
return 0;
}