实验三最佳路径选取问题.docx

上传人:b****7 文档编号:10414110 上传时间:2023-02-11 格式:DOCX 页数:32 大小:81.17KB
下载 相关 举报
实验三最佳路径选取问题.docx_第1页
第1页 / 共32页
实验三最佳路径选取问题.docx_第2页
第2页 / 共32页
实验三最佳路径选取问题.docx_第3页
第3页 / 共32页
实验三最佳路径选取问题.docx_第4页
第4页 / 共32页
实验三最佳路径选取问题.docx_第5页
第5页 / 共32页
点击查看更多>>
下载资源
资源描述

实验三最佳路径选取问题.docx

《实验三最佳路径选取问题.docx》由会员分享,可在线阅读,更多相关《实验三最佳路径选取问题.docx(32页珍藏版)》请在冰豆网上搜索。

实验三最佳路径选取问题.docx

实验三最佳路径选取问题

实验三:

管道铺设施工的最佳方案问题

一、问题描述

1、实验题目:

需要在某个城市n个居民区之间铺设煤气管道,则在这n个居民小区之间只需要铺设n-1条管道即可。

假设任意两个校区之间都可以铺设管道,但由于地理环境不同,所需要的费用也不尽相同。

选择最优的方案能使总投资尽可能小,这个问题即为求无向网的最小生成树。

2、基本要求:

在可能假设的m条管道中,选取n-1条管道,使得既能连同n个小区,又能使总投资最小。

每条管道的费用以网中该边的权值形式给出,网的存储采用邻接表的结构。

3、测试数据:

使用下图给出的无向网数据作为程序的输入,求出最佳铺设方案。

 

 

图为小区煤气管道铺设网及其参考解

数据输入与输出:

从键盘或文件读入上图中的无向网,以顶点对(i,j)的形式输出最小生成树的边。

二、需求分析

1、本程序通过用户提供的信息,可给出n个小区之间的管道铺设的最佳路径。

2、当用户输入必要的信息完毕后,提示用户输入小区的起点。

3、程序将自动给出以某个小区作为起点的最佳路径。

三、概要设计

1、设计思路

这是一个运筹学中的规划问题,求解最佳方案。

先选一个点作为起点,从这个点选取费用最小的一条路径。

算法的基本思想是:

对于图G=(V,E),V是包含n个顶点的顶点集,E是包含m条弧的弧集,(v,w)是E中从v到w的弧,c(v,w)是弧(v,w)的非负权值,设s为V中的顶点,t为V中可由s到达的顶点,则求解从s至t的具有最小弧权值和的最短路径搜索过程如下:

①将v中的顶点分为3类:

已标记点、未标记点、已扫描点。

将s初始化为已标记点,其他顶点为未标记点。

为每个顶点v都建立一个权值d和后向顶点指针p,并将d初始化如下:

d(v)=0,v=s;d(v)=∞,v≠s。

②重复进行扫描操作:

从所有已标记点中选择一个具有最小权值的顶点v,并将其设为已扫描点,然后检测每个以v为顶点的弧(v,w),若满足d(v)+c(v)

③若终点t被设为已扫描点,则搜索结束。

由t开始遍历后向顶点指针p直至起点s,即获得最小费用路径解。

2、数据结构

对于无向网络,采用邻接表的结构存储。

①抽象数据类型

ADTR[N]{

数据对象:

D={

|

int,i=1,2,3....}

数据关系:

R=

}}ADTR[N]

定义一个数组a[],存储任意两点之间的权值。

定义一个数组b[],每个数组元素表示当前所找到的从始点vi到vk最小费用的值。

它的初态为:

若从vi到vj有边,则为边的权值;否则置为∞。

定义一个数组fee[],其元素fee[k]用以记录vi到vk最短路径中vk的直接前驱结点的序号,如果vi到vk存在边,则fee[k]初值为i。

定义一个数组tag[],记录某个顶点是否已计算过最小费用,如果tag[k]=0,则vk

V-S,否则vk

S。

初始值除tag[i]=1外,所有值均为0。

②边表结点结构体

EdgeNode//边表结点

{

定义一个访问标记boolflag;

顶点域charname;

顶点所能达到的结点chars_name;

边上信息doublefee;

结点的指针域EdgeNode*next;

};

③顶点表结构体

Vnode//顶点表结点

{

顶点域charvname;

边表的头指针EdgeNode*firstedge;

};

④无向网图的结构体

Algraph

{

邻接表类型的数组VnodeAdjList[MaxVerNum];

邻接表的顶点数与边数intn,e;

};

⑤自定义函数

voidCreate_Algraph(Algraph*G)//创建无向网图

{

根据边及顶点的个数

for()

{申请空间,并将相应的信息填入空间中}

}

voidPrint_Algraph(Algraph*G);//以邻接表的存储形式打印出网络的信息

voidChange_Flag(Algraph*G,charname)

{若结点被访问,就将flag修改为1;}

EdgeNode*Min_Fee(Algraph*G,charname)//求去最小费用的函数

{

对于一个顶点,若与其相连的顶点的费用,都被访问过(即flag=1)

那么就返回该结点的上一级结点

如果至少有一条路径没被访问,那么就将最小费用的结点返回;

}

voidMin_Tree(Algraph*G,Vnode*V,charname)/*求解最小生成树,name为

最小生成树的根节点*/

{

根据给出的根结点,并调用Min_Fee()函数,求取最小费用,在与其他结点的最小费用相比,在求出最小费用;

将最小费用的结点用链表的形式存储;

}

voidPrint_MinTree(EdgeNode*N);//打印最小生成树

doubleTotal_Fee(Vnode*V);//计算最小生成树对应的费用

⑥本程序的保护模块:

结构体模块

主程序模块

自定义函数模块

调用关系:

3、程序设计

主要算法的程序流程图:

Create_Algraph()的算法流程图:

Print_Algraph()的算法流程图:

 

Change_Flag()的算法流程图:

 

Min_Fee()的算法流程图:

Min_Tree()的算法流程图:

Print_MinTree()的算法流程图:

Total_Fee()的算法流程图:

四、详细设计

1、结构体的定义、全局变量及宏定义

#defineMaxVerNum9

#defineEdgeNodestructnode

#defineVnodestructvnode

#defineAlgraphstructalgraph

#defineM9//网络的顶点数

#defineE15//网络的边数

doublefee[15]={32.8,44.6,12.1,18.2,5.9,21.3,41.1,56.4,67.3,

98.7,85.6,10.5,79.2,52.5,8.7};

chara[15]={'A','A','A','A','B','C','C','C','D','D','E','E','F','G','H'};

charb[15]={'B','C','H','I','C','D','E','G','E','F','F','G','I','H','I'};

/*定义全局变量,这三个数组是将网络信息录入,不用手动输入,当网络有所变化时,只需将数组内部的内容改动即可*/

//注意:

这里节点名称要以大写字母表示,且从A开始

2、自定义函数

voidCreate_Algraph(Algraph*G)//创建无向网图

{inti;

EdgeNode*s,*t;

G->n=M;

G->e=E;

for(i=0;in;i++)

{

G->AdjList[i].vname=(char)(i+65);

G->AdjList[i].firstedge=NULL;

}

for(i=0;ie;i++)

{

s=(EdgeNode*)malloc(sizeof(EdgeNode));

t=(EdgeNode*)malloc(sizeof(EdgeNode));

s->name=a[i];

s->fee=fee[i];

s->flag=0;

s->next=G->AdjList[(int)b[i]-65].firstedge;

G->AdjList[(int)b[i]-65].firstedge=s;

t->name=b[i];

t->fee=fee[i];

t->flag=0;

t->next=G->AdjList[(int)a[i]-65].firstedge;

G->AdjList[(int)a[i]-65].firstedge=t;

}

//下面是手动输入网络信息的代码

/*inti;

chara,b;

floatf;

EdgeNode*s,*t;

for(i=0;i<9;i++)

{

G->AdjList[i].vname=(char)(i+65);

G->AdjList[i].firstedge=NULL;

}

cout<<"请输入小区的个数:

"<

cin>>G->n;

cout<<"可进行管道铺设的边数:

"<

cin>>G->e;

cout<<"请输入小区代号(输入格式为:

小区号):

"<

for(i=0;in;i++)

{

cin>>G->AdjList[i].vname;

G->AdjList[i].firstedge=NULL;

}

cout<<"请输入两个小区之间铺设管道的费用(输入格式:

A(空格)B(空格)费用):

"<

for(i=0;ie;i++)

{

cin>>a>>b>>f;

s=(EdgeNode*)malloc(sizeof(EdgeNode));

t=(EdgeNode*)malloc(sizeof(EdgeNode));

s->name=b;

s->fee=f;

s->flag=0;

s->next=G->AdjList[(int)a-65].firstedge;

G->AdjList[(int)a-65].firstedge=s;

t->name=a;

t->fee=f;

t->flag=0;

t->next=G->AdjList[(int)b-65].firstedge;

G->AdjList[(int)b-65].firstedge=t;

}*/

}

voidPrint_Algraph(Algraph*G)//以邻接表的形式打印出网络的信息

{inti;

EdgeNode*s;

for(i=0;in;i++)

{

s=G->AdjList[i].firstedge;

cout<AdjList[i].vname<<"\t-->"<<'\t';

while(s)

{

cout<name<<"("<fee<<")"<<'\t';

s=s->next;

}

cout<

}

}

voidChange_Flag(Algraph*G,charname)//若结点被访问,就将flag修改为1;

{EdgeNode*N;

inti;

for(i=0;in;i++)

{

N=G->AdjList[i].firstedge;

while(N)

{

if(N->name==name)

{

N->flag=1;

break;

}

N=N->next;

}

}

}

EdgeNode*Min_Fee(Algraph*G,charname)

{//返回某个结点对应的最小费用的结点

EdgeNode*N,*s;

inti;

for(i=0;in;i++)

{

if(G->AdjList[i].vname==name)

break;

}

N=G->AdjList[i].firstedge;

while(N->flag&&N->next)

N=N->next;

s=N;

while(N)

{

if(N->feefee&&N->flag==0)

s=N;

N=N->next;

}

returns;

}

voidMin_Tree(Algraph*G,Vnode*V,charname)/*求解最小生成树,name为最小生成树的根节点*/

{

EdgeNode*start,*s,*t,*p,*q,*N;

inti;

for(i=0;in;i++)

{

if(G->AdjList[i].vname==name)

break;

}

start=(EdgeNode*)malloc(sizeof(EdgeNode));

start->name=name;

start->s_name=name;

start->fee=0;

start->next=V->firstedge;

V->firstedge=start;

Change_Flag(G,name);

for(i=0;in-1;i++)

{

t=V->firstedge;

if(Min_Fee(G,t->name)->flag==1)

{/*若果Min_Fee返回的节点对应的邻接表中结点均被访问过,

那么就在最小生成树的结点表中找下一个结点*/

while(Min_Fee(G,t->name)->flag==1)

t=t->next;

}

s=t;

while(t)

{

p=Min_Fee(G,s->name);

if((p->fee)>(q->fee)&&q->flag==0&&p->flag==0)

s=t;

t=t->next;

}

N=(EdgeNode*)malloc(sizeof(EdgeNode));

N->s_name=s->name;

N->name=Min_Fee(G,s->name)->name;

N->fee=Min_Fee(G,s->name)->fee;

N->next=V->firstedge;

V->firstedge=N;

Change_Flag(G,N->name);

}

}

voidPrint_MinTree(EdgeNode*N)//打印最小生成树

{

if(N->next)

{

Print_MinTree(N->next);

cout<s_name<<"--("<fee<<")--"<name<

}

}

doubleTotal_Fee(Vnode*V)

{//计算最小费用

EdgeNode*N;

N=V->firstedge;

doublesum=0;

while(N->next)

{

sum+=N->fee;

N=N->next;

}

returnsum;

}

3、主函数

intmain()

{

charstart_name;

Vnode*V;

Algraph*G;

V=(Vnode*)malloc(sizeof(Vnode));

V->firstedge=NULL;

G=(Algraph*)malloc(sizeof(Algraph));

cout<

cout<

Create_Algraph(G);

cout<<"各个小区之间可供选择的铺设路径及费用如下:

"<

Print_Algraph(G);

cout<<"请输入起始点的小区号:

"<

cin>>start_name;

Min_Tree(G,V,start_name);

cout<<"铺设管道的最佳路径为:

"<

Print_MinTree(V->firstedge);

cout<<"最佳路径的总费用为:

"<

return0;

}

4、函数的调用关系

主函数main()调用自定义函数voidCreate_Algraph(Algraph*G)/*创建邻接表*/,voidPrint_Algraph(Algraph*G)/*以邻接表的形式打印出网络的信息*/,voidMin_Tree(Algraph*G,Vnode*V,charname);/*求解最小生成树,name为最小生成树的根节点*/,voidPrint_MinTree(EdgeNode*N);/*打印最小生成树*/,doubleTotal_Fee(Vnode*V)等等。

voidMin_Tree(Algraph*G,Vnode*V,charname);/*求解最小生成树,name为最小生成树的根节点*/调用了voidChange_Flag(Algraph*G,charname);/*若结点被访问,就将flag修改为1*/;EdgeNode*Min_Fee(Algraph*G,charname);/*求结点的最小费用*/等等。

五、调试分析

1、为了方便调试,所以将小区间管道铺设的信息当做全局变量放在程序中,直接输出,省去每次调试都要输入很多信息。

2、在调试的过程中,会遇到很多问题,主要是因为邻接表的结构有点复杂,操作起来容易出错,查找错误的过程就很困难,但是,通过耐心的调试,还是得到了最终的结果。

3、算法的时空分析

无向网的顶点表是一个数组,这个数组的每个元素有一个数据域和一个指针域,其指针域的指向是一个链表。

所以,在查找顶点域中的数据时,时空复杂度较低,在查找链表中的数据时,就略有些麻烦。

4、该编写的程序存在的缺陷是:

小区的顶点只能以大写字母来表示,而且必须从A开始,依次向下排,不具有普适性。

六、使用说明

1、该程序将无向网的信息当做全局变量,避免多次调试引起多次大量数据的输入,对于小区的名称,必须用大写字母来表示,而且必须从A开始,依次向下排。

2、用户根据提示输入最小生成树的根结点。

3、程序将自动给出最小生成树的边,及最佳方案的总费用。

 

七、结果

1、以A为最小生成树的根结点的调试结果:

2、以C为最小生成树的根结点

八、附录

源程序清单:

#defineMaxVerNum9

#defineEdgeNodestructnode

#defineVnodestructvnode

#defineAlgraphstructalgraph

#defineM9//网络的顶点数

#defineE15//网络的边数

doublefee[15]={32.8,44.6,12.1,18.2,5.9,21.3,41.1,56.4,67.3,

98.7,85.6,10.5,79.2,52.5,8.7};

chara[15]={'A','A','A','A','B','C','C','C','D','D','E','E','F','G','H'};

charb[15]={'B','C','H','I','C','D','E','G','E','F','F','G','I','H','I'};

/*定义全局变量,这三个数组是将网络信息录入,不用手动输入,当网络有所变化时,只需将数组内部的内容改动即可*/

//注意:

这里节点名称要以大写字母表示,且从A开始

voidCreate_Algraph(Algraph*G);//创建邻接表

voidPrint_Algraph(Algraph*G);//以邻接表的形式打印出网络的信息

voidChange_Flag(Algraph*G,charname);//若结点被访问,就将flag修改为1;

EdgeNode*Min_Fee(Algraph*G,charname);

voidMin_Tree(Algraph*G,Vnode*V,charname);//求解最小生成树,name为最小生成树的根节点

voidPrint_MinTree(EdgeNode*N);//打印最小生成树

doubleTotal_Fee(Vnode*V);

intmain()

{

charstart_name;

Vnode*V;

Algraph*G;

V=(Vnode*)malloc(sizeof(Vnode));

V->firstedge=NULL;

G=(Algraph*)malloc(sizeof(Algraph));

cout<

cout<

Create_Algraph(G);

cout<<"各个小区之间可供选择的铺设路径及费用如下:

"<

Print_Algraph(G);

cout<<"请输入起始点的小区号:

"<

cin>>start_name;

Min_Tree(G,V,start_name);

cout<<"铺设管道的最佳路径为:

"<

Print_MinTree(V->firstedge);

cout<<"最佳路径的总费用为:

"<

return0;

}

voidCreate_Algraph(Algraph*G)//创建无向网图

{inti;

EdgeNode*s,*t;

G->n=M;

G->e=E;

for(i=0;in;i++)

{

G->AdjList[i].vname=(char)(i+65);

G->AdjList[i].firstedge=NULL;

}

for(i=0;ie;i++)

{

s=(EdgeNode*)malloc(sizeof(EdgeNode));

t=(EdgeNode*)malloc(sizeof(EdgeNode));

s->name=a[i];

s->fee=fee[i];

s->flag=0;

s->next=G->AdjList[(int)b[i]-65].firstedge;

G->AdjList[(int)b[i]-65].firstedge=s;

t->name=b[i];

t->fee=fee[i];

t->flag=0;

t->next=G->AdjList[(int)a[i]-65].firstedge;

G->AdjList[(int)a[i]-65].firstedge=t;

}

//下面是手动输入网络信息的代码

/*inti;

chara,b;

floatf;

EdgeNode*s,*t;

for(i=0;i<9;i++)

{

G->AdjList[i].vname=(char)(i+65)

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 英语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1