1表示已建,0表示未建。
当N为0时输入结束。
输出参数:
每个测试用例占输出的一行,输出实现后所需的最小成本值
1.2问题解决方案
第一种方案:
使用Prim算法
首先以一个结点作为最小生成树的初始结点,然后以迭代的方式找出与最小生成树中各结点权重最小边,并加入到最小生成树中。
加入之后如果产生回路则跳过这条边,选择下一个结点。
当所有结点都加入到最小生成树中之后,就找出了连通图中的最小生成树了。
第二种方案:
使用kruskal算法
算法过程:
1.将图各边按照权值进行排序2.将图遍历一次,找出权值最小的边,(条件:
此次找出的边不能和已加入最小生成树集合的边构成环),若符合条件,则加入最小生成树的集合中。
不符合条件则继续遍历图,寻找下一个最小权值的边。
3.递归重复步骤1,直到找出n-1条边为止(设图有n个结点,则最小生成树的边数应为n-1条),算法结束。
得到的就是此图的最小生成树。
第二章概要设计
2.1抽象数据类型
ADTGraph{
数据对象V:
V是具有相同特性的数据元素的和,称为顶点级。
数据关系R:
R={VR}
VR={|v,w
V且P(v,w),表示从v到w的弧,
谓词P(v,w)定义了弧的意义或信息}
基本操作:
CreatGraph(&G,V)
初始条件:
v是顶点数量
操作结果:
构造含有V个顶点的图G
Prim(G)
初始条件:
图G存在
操作结果:
找到图G中最小生成树
Kruskal(G)
初始条件:
图G存在
操作结果:
找到图G中最小生成树
}ADTGraph
2.2主要算法描述
2.1.1Prim算法实现
2.2.2Kruskal算法实现
2.3主要算法分析
Prim算法时间复杂度为O(n2),与图中顶点个数有关,而与边数无关。
Kruskal算法时间复杂度为O(eloge),与途中的边数有关,而与顶点数无关。
第三章详细设计
3.1程序代码
3.1.1Prim算法实现
#include
#defineNUM200//最大顶点数
#defineMAX40000//最大值
//定义邻接矩阵的结构
typedefstruct
{
intedges[NUM][NUM];//邻接矩阵
intn;//顶点数
inte;//边数
}MGraph;
//构造邻接矩阵,v表示顶点数
voidCreatGraph(MGraph&G,intv)
{
G.n=v;
G.e=v*(v-1)/2;
for(inti=1;i<=G.n;i++){//初始化邻接矩阵
for(intj=1;j<=G.n;j++){
G.edges[i][j]=MAX;
}
}
for(inti=1;i<=G.e;i++){//构造邻接矩阵
//v1,v2一条路连接的两村庄,cost修路费用,build修建状态
intv1,v2,cost,build;
scanf("%d%d%d%d",&v1,&v2,&cost,&build);
if(build==1)
cost=0;
G.edges[v1][v2]=cost;
G.edges[v2][v1]=cost;
}
}
intPrim(MGraphG)
{
intresult=0;//用于记录修路成本
intlowcost[NUM];//存放生成树顶点集合内顶点到生成树外各顶点的各边上的当前最小权值
intvex[NUM];//记录生成树顶点集合外各顶点距离集合内哪个顶点最近(即权值最小)
for(inti=1;i<=G.n;i++){//初始化lowcost[],vex[]
lowcost[i]=G.edges[1][i];
vex[i]=1;
}
vex[1]=-1;//加到生成树顶点集合
for(inti=2;i<=G.n;i++){//循环n-1次,加入n-1条边
intmin=MAX;
intk;//存放最小生成树结点的变量
for(intj=1;j<=G.n;j++){
if(vex[j]!
=-1&&lowcost[j]min=lowcost[j];//求生成树外顶点到生成树内顶点具有最
k=j;//小权值的边,k是当前具最小权值的边
}
}
result=result+lowcost[k];//将最小权值记录
vex[k]=-1;//该边加入生成树标记
for(intj=1;j<=G.n;j++){//修改lowcost[],vex[]
if(vex[j]!
=-1&&G.edges[k][j]lowcost[j]=G.edges[k][j];
vex[j]=k;
}
}
}
returnresult;
}
intmain()
{
while(true){
intv;
scanf("%d",&v);
if(v==0)break;
MGraphG;
CreatGraph(G,v);
intcost=Prim(G);
printf("%d\n",cost);
}
return0;
}
3.1.2Kruskal算法实现
#include
#defineNUM200//最大顶点个数
#defineEDGENUM20000//最大的边数
#defineMAX40000//最大值
//定义邻接矩阵的结构
typedefstruct
{
intedges[NUM][NUM];//邻接矩阵
intn;//顶点数
inte;//边数
}MGraph;
//定义边的结构
typedefstruct
{
intbegin;//边的起点
intend;//边的终点
intweight;//边的权值
}Edge;
//构造邻接矩阵,v表示顶点数
voidCreatGraph(MGraph&G,intv)
{
G.n=v;
G.e=v*(v-1)/2;
for(inti=1;i<=G.n;i++){//初始化邻接矩阵
for(intj=1;j<=G.n;j++){
G.edges[i][j]=MAX;
}
}
for(inti=1;i<=G.e;i++){//构造邻接矩阵
//v1,v2一条路连接的两村庄,cost修路费用,build修建状态
intv1,v2,cost,build;
scanf("%d%d%d%d",&v1,&v2,&cost,&build);
if(build==1)
cost=0;
G.edges[v1][v2]=cost;
G.edges[v2][v1]=cost;
}
}
//找顶点k的终点
intFind(intparent[],intk)
{
while(parent[k]>0){
k=parent[k];
}
returnk;
}
//Kruskal算法实现
intKruskal(MGraphG)
{
Edgeedge[EDGENUM];//图对应的所有边
intparent[EDGENUM];//用于记录顶点的终点
intk=1;//用于记录边的数量
for(inti=1;i<=G.n;i++){//构建边
for(intj=i+1;j<=G.n;j++){
edge[k].begin=i;
edge[k].end=j;
edge[k].weight=G.edges[i][j];
k++;
}
}
//根据边的权值大小对边数组进行从小到大排序
for(inti=1;ifor(intj=i+1;jif(edge[i].weight>edge[j].weight){
inttemp;
temp=edge[i].begin;
edge[i].begin=edge[j].begin;
edge[j].begin=temp;
temp=edge[i].end;
edge[i].end=edge[j].end;
edge[j].end=temp;
temp=edge[i].weight;
edge[i].weight=edge[j].weight;
edge[j].weight=temp;
}
}
}
//初始化parent数组
for(inti=1;i<=G.n;i++){
parent[i]=0;
}
intm,n;
intresult=0;//用于记录修路成本
for(inti=1;i<=G.e;i++){
//判断遍历的边是存在环
m=Find(parent,edge[i].begin);
n=Find(parent,edge[i].end);
if(m!
=n){
parent[n]=m;
result=result+edge[i].weight;
}
}
returnresult;
}
intmain()
{
while(true){
intv;
scanf("%d",&v);
if(v==0)break;
MGraphG;
CreatGraph(G,v);
intcost=Kruskal(G);
printf("%d\n",cost);
}
return0;
}
第四章调试分析
4.1出现的问题及解决方法
1.在调试过程中,为观察最短路径,输出了最短路径及权值:
2.在用Kruskal算法实现时,未考虑产生环的问题,而出现结果错误。
增加了查找边依附顶点的终点函数Find(),最后运行结果正确。
第五章测试分析
5.1测试样例
测试样例1:
Prim算法
图5-1
测试样例2:
Kruskal算法
图5-2