您的位置:首页 > 其它

(最小生成树问题:Prim,Kruskal)村村通公路

2017-08-21 17:51 351 查看
3362数据结构实验之图论六:村村通公路

Problem Description

当前农村公路建设正如火如荼的展开,某乡镇政府决定实现村村通公路,工程师现有各个村落之间的原始道路统计数据表,表中列出了各村之间可以建设公路的若干条道路的成本,你的任务是根据给出的数据表,求使得每个村都有公路连通所需要的最低成本。

Input

连续多组数据输入,每组数据包括村落数目N(N <= 1000)和可供选择的道路数目M(M <=

3000),随后M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个村庄的编号和修建该道路的预算成本,村庄从1~N编号。

Output

输出使每个村庄都有公路连通所需要的最低成本,如果输入数据不能使所有村庄畅通,则输出-1,表示有些村庄之间没有路连通。

Example Input

5 8
1 2 12
1 3 9
1 4 11
1 5 3
2 3 6
2 4 9
3 4 4
4 5 6


Example Output

19


解题思路

构造最小生成树的算法有普里姆算法(Prim)和克鲁斯卡尔算法(Kruskal)

1.普里姆算法(Prim):归并顶点(加顶点法),与边数无关,适于稠密图;

2.克鲁斯卡尔算法(Kruskal):归并边(加边法),适于稀疏图。实现过程借助并查集。

并查集知识补充如下。

并查集详解

代码1:普里姆算法(Prim)

/*普利姆算法(加点法):  邻接矩阵储存
在一定范围内,找到距离最短。
从村庄1开始,寻找与村庄1连通的有最小权值的村庄,之后再从该村庄找下一个有最小权值的村庄。*/
#include<stdio.h>
#include<string.h>
#define INF 2147483647    //表示村庄之间不连接
int vis[10000];
/*vis 访问标志数组 ——记录其他村庄集合
与已选的村庄集合哪个的成本最低。*/
int dis[101][101];
/*dis 存储相邻两村庄的权值。*
4000
/
int lowc[10000];
/*lowc 记录其他村庄集合与已选的村庄集合的最低的成本*/
int prime(int cost[][101],int n)
//普利姆  cost数组存储着dis数组数据
{
int i,j,p; //p用来记录每一次循环找到的节点的编号
int minc,res=0;
memset(vis,0,sizeof(vis));  //vis数组初始化为0
for(i=1;i<=n;i++)
//记录从村庄1(1 2 3 4 5)到村庄n的最低成本
{
lowc[i]=cost[1][i];//我们从1号节点开始生成树
}
vis[1]=1;  //对生成的树根标记访问
for(i=1;i<n;i++)    //要生成n-1条边,所以循环n-1次
{
minc=INF;
//p=-1;
for(j=1;j<=n;j++)
{
if(!vis[j]&&lowc[j]<minc)   //遍历找到村庄1(1 2 3 4 5)到其他村庄的最低成本
{
minc=lowc[j];
p=j;        //p记录下一个村庄代替当前村庄
}
}
if(INF==minc)//如果在此跳出循环,表示有城市之间不能连通
return -1;     //此图不连通
res+=minc;//加上找到的最小权值
vis[p]=1;   //标记找到的该节点被访问
for(j=1;j<=n;j++)  //更新lowc数组
{
if(vis[j]==0&&lowc[j]>cost[p][j])
{
lowc[j]=cost[p][j];//如果该点还没有被访问过,
//更新生成树到该点的距离;
}
}
}
return res;
}
int main()
{
int m,n,i,j,x,y,w;
while(scanf("%d %d",&n,&m)!=EOF)  //输入村落数目n和道路数目m 多组输入
{
for(i=1;i<=n;i++)           //初始化dis数组,若是同一村落为0,不是先设为最大
{
for(j=1;j<=n;j++)
{
if(j==i)
{
dis[i][j]=0;
}
else
{
dis[i][j]=INF;
}
}
}
for(i=0;i<m;i++)
{
scanf("%d %d %d",&x,&y,&w);  //依次输入村庄编号和预算成本
if(dis[x][y]>w)
{
dis[x][y]=w;            //无向图,预算成本赋值
dis[y][x]=w;
}
}
printf("%d\n",prime(dis,n));    //调用prime
}
}


代码2:克鲁斯卡尔算法(Kruskal)

#include<cstdio>
#include<stdlib.h>
#include<algorithm>
using namespace std;
int parent[1001];
typedef struct Node{
int u,v,info;
}Node;
Node cla[3001];
int cost;
/*int fnd(int t)
{
if(t!=parent[t])
return fnd(parent[t]);
return t;
}*/
int fnd(int t)
{
int r=t;
while(parent[r]!=r)
r=parent[r];
int i=t,j;
while(i!=r)//压缩路径
{
j=parent[i];
parent[i]=r;
i=j;
}
return r;
}
void Kruskal(Node s)
{
int fx=fnd(s.u);
int fy=fnd(s.v);
if(fx!=fy)
{
parent[fx]=fy;
cost+=s.info;
//printf("fx=%d,fy=%d,cost=%d\n",fx,fy,cost);
}
}
bool cmp(Node A,Node B)
{
return A.info<B.info;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
int i,cnt=0;
cost=0;
for(i=1;i<=n;i++)
{
parent[i]=i;
}
for(i=0;i<m;i++)
{
scanf("%d%d%d",&cla[i].u,&cla[i].v,&cla[i].info);
}
sort(cla,cla+m,cmp);
for(i=0;i<m;i++)
{
Kruskal(cla[i]);
}
for(i=1;i<=n;i++)
{
if(parent[i]==i)
cnt++;
}
if(cnt!=1)
printf("-1\n");
else
printf("%d\n",cost);
}
return 0;
}


利用qsort()排序。

#include<cstdio>
#include<stdlib.h>
using namespace std;
int parent[1001];
typedef struct Node{
int u,v,info;
}Node;
Node cla[3001];
int cost;
int fnd(int t)
{
int r=t;
while(parent[r]!=r)
r=parent[r];
int i=t,j;
while(i!=r)//压缩路径
{
j=parent[i];
parent[i]=r;
i=j;
}
return r;
}
void Kruskal(Node s)
{
int fx=fnd(s.u);
int fy=fnd(s.v);
if(fx!=fy)
{
parent[fx]=fy;
cost+=s.info;
// printf("fx=%d,fy=%d,cost=%d\n",fx,fy,cost);
}
}
int cmp(const void *a,const void *b)        //按村庄之间的预算成本从小到大排序
{
return (*(Node *)a).info>(*(Node *)b).info?1:-1;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
int i,cnt=0;
cost=0;
for(i=1;i<=n;i++)
{
parent[i]=i;
}
for(i=0;i<m;i++)
{
scanf("%d%d%d",&cla[i].u,&cla[i].v,&cla[i].info);
}
qsort(cla,m,sizeof(cla[0]),cmp);    //qsort排序
/*1 待排序数组首地址
2 数组中待排序元素数量
3 各元素的占用空间大小
4 指向函数的指针,用于确定排序的顺序*/
for(i=0;i<m;i++)
{
Kruskal(cla[i]);
}
for(i=1;i<=n;i++)
{
if(parent[i]==i)
cnt++;
}
if(cnt!=1)
printf("-1\n");
else
printf("%d\n",cost);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: