TSP问题的解决与实现Word格式文档下载.docx

上传人:b****5 文档编号:21339515 上传时间:2023-01-29 格式:DOCX 页数:28 大小:91.64KB
下载 相关 举报
TSP问题的解决与实现Word格式文档下载.docx_第1页
第1页 / 共28页
TSP问题的解决与实现Word格式文档下载.docx_第2页
第2页 / 共28页
TSP问题的解决与实现Word格式文档下载.docx_第3页
第3页 / 共28页
TSP问题的解决与实现Word格式文档下载.docx_第4页
第4页 / 共28页
TSP问题的解决与实现Word格式文档下载.docx_第5页
第5页 / 共28页
点击查看更多>>
下载资源
资源描述

TSP问题的解决与实现Word格式文档下载.docx

《TSP问题的解决与实现Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《TSP问题的解决与实现Word格式文档下载.docx(28页珍藏版)》请在冰豆网上搜索。

TSP问题的解决与实现Word格式文档下载.docx

最短路径为v0v1v4v2v3

2.需求分析

(1)本程序用于求解n个结点的最短哈密尔顿回路问题。

(2)程序运行后显示提示信息—“Pleaseinsertthenumberofcities:

”,例如用户输入5,则表示结点n的值为5;

接下来程序输出提示信息—“Pleaseinsertthedistancebetweenonecityandanother:

”,例如用户输入测试数据中给出的路程矩阵,表示任意两个城市之间的距离,比如第一个城市到第0个城市之间的距离为25。

(3)用户输入数据完毕,程序将输出运算结果。

(4)测试数据均为正数,其中用999来表示两个城市之间距离为∞。

3.概要设计

为了实现上述程序功能,使用优先队列来维护结点表,因此需要图和队列两个抽象数据类型。

(1)图的抽象数据类型定义

ADTGraph{

Data:

具有相同类型的数据元素的集合,称为顶点集。

Relation:

顶点偶对的有穷集合。

Operation:

CreateGraph(&

G,V,VR)

初始条件:

V是图中顶点集合,VR是图中顶点偶对集合。

操作条件:

按照V和VR的定义构造图G。

DestroyGraph(&

G)

图G已经存在。

操作结果:

销毁G。

LocateVex(G,u)

图G已经存在,u和G中顶点有相同类型。

操作结果:

如果G中存在u,则返回u在G中的位置;

否则返回相应信息。

GetVex(G,v)

图G已经存在,v是G中某个顶点。

返回v的值。

PutVex(&

G,v,value)

图G已经存在,v是G中顶点。

对v赋值value。

FirstAdjvex(G,v)

返回v的第一个邻接顶点。

如果v在G中没有邻接顶点,则返回相应信息。

NextAdjvex(G,v,w)

图G已经存在,v是G中顶点,w是v的邻接顶点。

返回v的下一个邻接顶点。

如果w是v的最后一个邻接点,则返回相应信息。

InsertVex(&

G,v,w)

图G已经存在,v和w是G中顶点。

在G中添加v。

DeleteVex(&

G,v)

删除G中顶点v及其相关的边。

InsertEdge(&

在G中添加<

v,w>

如果G是无向图,则还增添<

w,v>

DeleteEdge(&

在G中删除<

;

如果G是无向图,则还删除<

DFSTraverse(G,v)

从v起深度访问G。

BFSTraverse(G,v)

从v起广度访问G。

}ADTGraph

(2)队列的抽象数据类型

ADTQueue{

Data:

具有相同数据类型的及先进先出特性的数据元素集合。

Relation:

相邻数据元素具有前驱和后继的关系。

InitQueue(&

Q)

创造一个空队列Q。

DestroyQueue(&

队列Q已经存在。

销毁Q。

ClearQueue(&

重置Q为空队列。

QueueLength(Q)

返回Q的元素个数。

GetHead(Q,&

e)

队列Q已经存在并且非空。

用e返回Q的队头元素。

EnQueue(&

Q,e)

DeQueue(&

Q,&

队列Q已经存在且非空。

删除Q的队头元素,并用e返回其值。

}ADTQueue

(3)本程序包含三大模块:

主程序模块,TSP算法模块,辅函数模块。

三大模块之间的调用关系如下。

4.详细设计

(1)元素类型、结点类型和指针类型、变量和数据结构声明

Node*xnode;

//父亲结点指针

Node*ynode;

//儿子结点指针

Node*znode;

Node*qbase;

//优先队列首指针

ElemTypebound;

//当前可行解的最优值

typedefintElemType;

//元素类型

#defineMAX_VALUE_OF_TYPE999;

//代表∞

城市顶点用数字0,1,2,……,n-1编号。

在搜索的过程中,各个结点的数据是动态变化的,互不相同,发生回溯时,必须使用结点中原来的数据。

因此,每个结点的数据必须是局部与该结点的。

用如下的数据结构来定义结点中所使用的数据:

typedefstructNode{

ElemTypec[100][100];

//路程矩阵

intinit_row[100];

//路程矩阵的当前行映射为原始行

intinit_col[100];

//路程矩阵的当前列映射为原始列

intcur_row[100];

//路程矩阵的原始行映射为当前行

intcur_col[100];

//路程矩阵的原始列映射为当前列

intad[100];

//回路顶点邻接表

intk;

//当前路程矩阵的阶

ElemTypew;

//结点的下界

structNode*next;

//队列链指针

}Node;

(2)队列类型

typedefstructQNode{

Node*data;

//数据域

structQNode*next;

//指针域

}QNode,*QueuePtr;

typedefstruct{

QueuePtrfront;

//头指针,指向链队头结点

QueuePtrrear;

//尾指针,指向链队列最后一个结点

}LinkQueue;

程序中所用到的关于优先队列基本操作实现的伪码算法如下:

voidInitQueue(LinkQueue&

Q){

//构造一个空链队列Q

Q.front=Q.rear=newQNode;

Q.front->

next=NULL;

}//InitQueue

voidEnQueue(LinkQueue&

Q,Node*e){

//插入一个指针e到链队列Q中,成为新的队尾指针

QueuePtrp;

p=newQNode;

p->

data=e;

Q.rear->

next=p;

Q.rear=p;

}//EnQueue

Node*DeQueue(LinkQueue&

//若链队列Q为空,则返回NULL;

否则返回指向数据的指针

QNode*p;

Node*e;

if(Q.front->

next==NULL)returnNULL;

p=Q.front->

next;

e=p->

data;

next=p->

if(Q.rear==p)Q.rear=Q.front;

deletep;

returne;

}//DeQueue

(3)辅助函数的实现(共7个)

ElemTypeRow_min(Node*node,introw,ElemType&

second){

//计算路程矩阵行的最小值

if(node->

c[row][0]<

node->

c[row][1]){

temp=node->

c[row][0];

second=node->

c[row][1];

}

else{

for(i=2;

i<

k;

i++){

c[row][i]<

temp){

second=temp;

c[row][i];

elseif(node->

second)

returntemp;

ElemTypeCol_min(Node*node,intcol,ElemType&

//计算路程矩阵列的最小值

c[0][col]<

c[1][col]){

c[0][col];

c[1][col];

c[i][col]<

c[i][col];

ElemTypeArray_red(Node*node){

//归约node所指向的结点的路程矩阵

sum=0;

for(i=0;

i++){//行归约

temp=Row_min(node,i,temp1);

//行归约常数

for(j=0;

j<

j++)

node->

c[i][j]-=temp;

sum+=temp;

//行归约常数累计

for(j=0;

j++){//列归约

temp=Col_min(node,j,temp1);

//列归约常数

i++)

returnsum;

ElemTypeEdge_sel(Node*node,int&

vk,intv1){

//计算Dkl,选择搜索分支的边

d=0;

ElemType*row_value=newElemType[node->

k];

ElemType*col_value=newElemType[node->

i++)//每一行的次小值

Row_min(node,i,row_value[i]);

for(i=0;

i++)//每一列的次小值

Col_min(node,i,col_value[i]);

i++){//对路程矩阵所有的0元素值

j++){//计算相应的temp值

c[i][j]==0){

temp=row_value[i]+col_value[i];

if(temp>

d){//求最大的temp值于d

d=temp;

vk=i;

v1=j;

}//保存相应的行、列号

deleterow_value;

deletecol_value;

returnd;

voidDel_rowcol(Node*node,intvk,intv1){

//删除路程矩阵的第vk行和第v1列的所有元素

for(i=vk;

k-1;

i++)//元素上移

v1;

c[i][j]=node->

c[i+1][j];

for(j=v1;

j++)//元素左移

vk;

c[i][j+1];

for(i=vk;

i++)//元素上移及左移

for(j=v1;

j++)

c[i+1][j+1];

vk1=node->

init_row[vk];

//当前行转换为原始行vk1

row_cur[vk1]=-1;

//原始行vk1置删除标志

for(i=vk1+1;

n;

i++)//vk1之后的原始行,其对应的当前行号减1

row_cur[i]--;

vl1=node->

init_col[v1];

//当前列v1转换为原始列vl1

col_cur[vl1]=-1;

//原始列vl1置删除标志

for(i=vl1+1;

i++)//vl1之后的原始列,其对应的当前列号减1

col_cur--;

i++)//修改vk及其后的当前行的对应原始行号

init_row[i]=node->

init_row[i+1];

for(i=v1;

i++)//修改v1及其后的当前列的对应原始行号

node->

init_col[i]=node->

init_col[i+1];

k--;

//当前矩阵的阶数减1

voidEdge_byp(Node*node,intvk,intvl){

//登记回路顶点邻接表,旁路有关的边

vk=init_row[vk];

//当前行号转换为原始行号

vl=init_col[vl];

//当前列号转换为原始列号

ad[vk]=vl;

//登记回路顶点邻接表

k=node->

row_cur[vl];

//vl转换为当前行号k

l=node->

col_cur[vk];

//vk转换为当前列号l

if((k>

=0)&

&

(l>

=0))//当前行、列号均处于当前矩阵中

c[k][l]=MAX_VALUE_OF_TYPE;

//旁路相应的边

Node*Initial(ElemTypec[][],intn){

//初始化

Node*node=newNode;

//分配结点缓冲区

i++)//复制路程矩阵的初始数据

c[i][j]=c[i][j];

i++){//建立路程矩阵原始行、列号与初始行、列号对应的关系

init_row[i]=i;

init_col[i]=i;

row_cur=i;

col_cur=i;

i++)//回路顶点邻接表初始化为空

ad[i]=-1;

k=n;

returnnode;

//返回结点指针

(4)TSP问题分支限界算法

voidTSP(ElemTypec[100][100],intn,int*ad){

bound=MAX_VALUE_OF_TYPE;

InitQueue(qbase);

//初始化队列

xnode=Initial(c,n);

//初始化父亲结点--------x结点

xnode->

w=Array_red(xnode);

//归约路程矩阵

while(xnode->

k!

=0){

d=Edge_sel(xnode,vk,vl);

//选择分支方向并计算Dkl

znode=newNode;

//建立分支结点------z结点(右儿子结点)

*znode=*xnode;

//x结点数据复制给z结点

znode->

c[vk][vl]=MAX_VALUE_OF_TYPE;

//旁路结点的边

Array_red(znode);

//归约z结点路程矩阵

w=xnode->

w+d;

//计算z结点的下界

if(znode->

w<

bound)//若下界小于当前可行解最优值

EnQueue(qbase,znode);

//将z结点插入优先队列

elsedeleteznode;

//否则,剪去该结点

ynode=newNode;

//建立分支节点----y结点(左儿子结点)

*ynode=*xnode;

//x结点数据复制到y结点

Edge_byp(ynode,vk,vl);

//登记回路邻接表,旁路有关的边

Del_rowcol(ynode,vk,vl);

//删除结点y路程矩阵当前vk行vl列

ynode->

//归约y结点路程矩阵

w+=xnode->

w;

//计算y结点的下界

if(ynode->

k==2){//路程矩阵只剩2阶

if((ynode->

c[0][0]==0)&

(ynode->

c[1][1]==0)){

ad[ynode->

init_row[0]]=ynode->

init_col[0];

init_row[1]]=ynode->

init_col[1];

}//登记最后的两条边

ynode->

k=0;

bound){//若下界小于当前可行解最优值

EnQueue(qbase,ynode);

//y结点插入优先队列

if(ynode->

k==0)//更新当前可行解最优值

bound=ynode->

elsedeleteynode;

//否则剪去y结点

xnode=DeQueue(qbase);

//取优先队列首元素

//保存最短路线长度

i++)//保存路线的顶点邻接表

ad[i]=xnode->

ad[i];

deletexnode;

//释放x结点缓冲区

while(qbase.front!

=qbase.rear){//释放队列节点缓冲区

xnode=DeQueue(qbase);

deletexnode;

cout<

<

"

Thewayis:

for(intm=0;

m<

m++)

ad[m];

endl;

cout<

Thelengthofthewayis:

(5)主函数的伪码算法

voidmain(){

”Pleaseinsertthenumberofcities:

”;

cin>

>

”Pleaseinsertthedistancebetweenonecityandanother:

{cin>

c[i][j];

””;

TSP(c,n,ad);

}//main

(6)函数调用关系图

5.调试分析

(1)

使用分支限界法求解的过程中,将动态地生成很多结点,用结点表来存放动态生成的结点信息。

因此必须按路程的下界来确定搜索的方向,因此可以用优先队列或堆来维护结点表。

在此使用优先队列来维护结点表。

(2)算法的时间复杂度分析

TSP算法的时间估计如下:

a.初始化父亲结点,归约父亲结点路程矩阵,都需要O(n2)时间。

b.while循环体的执行次数取决于所搜索的结点个数,假定所搜索的借点数为c。

在while循环内部,选择分支方向需要O(n2)时间。

c.把x结点数据复制到z结点(这里包括整个路程矩阵的复制工作),归约z结点的路程矩阵,都需要O(n2)时间。

d.把z结点插入到优先队列,在最坏的情况下需要O(c)时间。

e.把x结点复制到y结点,同样需要O(n2)时间。

f.登记回路邻接表,旁路有关的边,只需O

(1)时间。

g.删除y结点路程矩阵当前vk行vl列,归约y结点的路程矩阵,这些操作都需要O(n2)时间。

h.把y结点插入队列,删除队列首元素,都需要O(c)时间。

i.其余的花费为O

(1)时间。

j.因此,整个while循环在最坏的情况下需要O(cn2)时间。

k.最后,在算法的尾部,for循环保存路线的顶点邻接表于数组ad中作为算法的返回值,需要O(n)时间。

l.释放队列缓冲区的while循环在最坏情况下需要O(c)时间。

所以,整个算法的时间复杂度为O(cn2)。

(3)算法的空间复杂度分析

算法所需要的空间,主要花费在结点的存储空间。

每个结点需要O(n2)空间存放

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

当前位置:首页 > 工程科技 > 环境科学食品科学

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

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