1、TSP问题的解决与实现报告1. 问题描述 所谓TSP问题是指旅行家要旅行n个城市,要求各个城市经历且仅经历一次,并且要求所走的路程最短。该问题又称为货郎担问题、邮递员问题、售货员问题,是图问题中最广为人知的问题。 2. 基本要求 (1) 上网查找TSP问题的应用实例; (2) 分析求TSP问题的全局最优解的时间复杂度; (3) 设计一个求近似解的算法; (4) 分析算法的时间复杂度。 3. 提交报告 课程设计报告提交内容包括: (1) 问题描述;(2) 需求分析;(3) 概要设计;(4) 详细设计;(5) 调试分析;(6) 使用说明;(7) 测试结果;(8) 附录(带注释的源程序)。 参见“数
2、据结构课程设计概述.pdf”和“数据结构课程设计示例.pdf”。 指导教师(签字): 系主任(签字): 批准日期:2014年 月 日 1问题描述 (1)题目要求 旅行家要旅行n个城市,要求各个城市经历且仅经历一次,最终要回到出发的城市,求出最短路径。 用图论的术语来说,假如有一个图G=(V,E),其中V是顶点集,E是边集,设D=(d)是由ij顶点i和顶点j之间的距离所组成的距离矩阵。TSP问题就是求出一条通过每个顶点且每个顶 点只通过一次的具有最短距离的回路。 )基本要求 (2 上网查找TSP问题的应用实例;a. 分析求TSP问题的全局最优解的时间复杂度;b. c. 设计一个求近似解的算法;
3、d. 分析算法的时间复杂度。 3)测试数据( TSP问题: 5个城市的 注:由于矩阵所表示的是两个城市之间的距离,所以该矩阵为对称矩阵 路程矩阵如图所示: 0 2 3 1 4 28 25 32 41 0 25 18 31 26 1 41 18 7 1 2 11 32 31 7 3 28 26 1 11 4 最短路径为vvvvv 341202.需求分析 (1)本程序用于求解n个结点的最短哈密尔顿回路问题。 (2)程序运行后显示提示信息“”,例如用户输入5,则表示结Please insert the number of cities:点n的值为5;接下来程序输出提示信息“Please insert
4、 the distance between one city and ”,例如用户输入测试数据中给出的路程矩阵,表示任意两个城市之间的距离,比another:如第一个城市到第0个城市之间的距离为25。 (3) 用户输入数据完毕,程序将输出运算结果。 (4)测试数据均为正数,其中用999来表示两个城市之间距离为。 3概要设计 为了实现上述程序功能,使用优先队列来维护结点表,因此需要图和队列两个抽象数据类 型。(1)图的抽象数据类型定义 ADT Graph Data: 具有相同类型的数据元素的集合,称为顶点集。 : Relation顶点偶对的有穷集合。 : Operation CreateGrap
5、h(&G,V,VR) 初始条件:是图中顶点集合,是图中顶点偶对集合。 VRV 操作条件:按照和的定义构造图。 VG VR DestroyGraph(&G) 初始条件:图已经存在。 G 操作结果:销毁。 G LocateVex(G,u) 初始条件:图已经存在,和中顶点有相同类型。 uGG 操作结果:如果中存在则返回在中的位置;否则返回相应信息。 Guu,G GetVex(G,v) 初始条件:图已经存在,是中某个顶点。 vGG 操作结果:返回的值。 v PutVex(&G,v,value) 初始条件:图已经存在,是中顶点。 GG v 操作结果:对赋值。 vvalue FirstAdjvex(G,v
6、) 初始条件:图已经存在,是中顶点。 v GG 操作结果:返回的第一个邻接顶点。如果在中没有邻接顶点,则返回相应信息。 vvG NextAdjvex(G,v,w) 初始条件:图已经存在,是中顶点是的邻接顶点。 G vG,wv 操作结果:返回的下一个邻接顶点。如果是的最后一个邻接点,则返回相应信息。 vw v InsertVex(&G,v,w) 初始条件:图已经存在,和是中顶点。 GwvG 操作结果:在中添加。 vG DeleteVex(&G,v) 初始条件:图已经存在,是中顶点。 GGv 操作结果:删除中顶点及其相关的边。 Gv,v,w) InsertEdge(&G初始条件:图已经存在,和是中
7、顶点。 wGGv 操作结果:在中添加;如果是无向图,则还增添。 GG DeleteEdge(&G,v,w) 初始条件:图已经存在,和是中顶点。 GwvG 操作结果:在中删除如果是无向图,则还删除。 GG; DFSTraverse(G,v) 初始条件:图已经存在,是中顶点。 GGv 操作结果:从起深度访问。 Gv BFSTraverse(G,v) 初始条件:图已经存在,是中顶点。 GG v 操作结果:从起广度访问。 vG ADT Graph (2)队列的抽象数据类型 ADT Queue Data: 具有相同数据类型的及先进先出特性的数据元素集合。 : Relation 相邻数据元素具有前驱和后继
8、的关系。 : Operation InitQueue(&Q) 初始条件:无 操作结果:创造一个空队列。 Q DestroyQueue(&Q) 初始条件:队列已经存在。 Q 操作结果:销毁。 Q ClearQueue(&Q) 初始条件:队列已经存在。 Q 操作结果:重置为空队列。 Q QueueLength(Q) 初始条件:队列已经存在。 Q 操作结果:返回的元素个数。 Q ,&e) GetHead(Q初始条件:队列已经存在并且非空。 Q 操作结果:用返回的队头元素。 eQEnQueue(&Q,e) 初始条件:队列已经存在。 Q 操作结果:重置为空队列。 Q ,&e) DeQueue(&Q初始条
9、件:队列已经存在且非空。 Q 操作结果:删除的队头元素,并用返回其值。 eQ ADT Queue (3) 本程序包含三大模块:主程序模块,TSP算法模块,辅函数模块。三大模块之间的调用关 系如下。 主函数模块 算法模块TSP 辅函数模块 详细设计4 元素类型、结点类型和指针类型、变量和数据结构声明(1) 父亲结点指针 / Node *xnode; /儿子结点指针 Node *ynode; 儿子结点指针 / Node *znode; 优先队列首指针 / Node *qbase; 当前可行解的最优值/ ;bound ElemType typedef int ElemType; /元素类型 #def
10、ine MAX_VALUE_OF_TYPE 999; /代表 城市顶点用数字0,1,2,n-1编号。在搜索的过程中,各个结点的数据是动态变化的,互不相同,发生回溯时,必须使用结点中原来的数据。因此,每个结点的数据必须是局部与该结点的。用如下的数据结构来定义结点中所使用的数据: typedef struct Node ElemType c100100; /路程矩阵 int init_row100; /路程矩阵的当前行映射为原始行 int init_col100; /路程矩阵的当前列映射为原始列 int cur_row100; /路程矩阵的原始行映射为当前行 int cur_col100; /路程
11、矩阵的原始列映射为当前列 int ad100; /回路顶点邻接表 int k; /当前路程矩阵的阶 ElemType w; /结点的下界 struct Node *next; /队列链指针 Node; (2)队列类型 typedef struct QNode Node *data; /数据域 struct QNode *next; /指针域 QNode,*QueuePtr; typedef struct QueuePtr front; /头指针,指向链队头结点 QueuePtr rear; /尾指针,指向链队列最后一个结点 LinkQueue; 程序中所用到的关于优先队列基本操作实现的伪码算法
12、如下: void InitQueue(LinkQueue &Q) /构造一个空链队列Q Q.front=Q.rear=new QNode; Q.front-next=NULL; /InitQueue void EnQueue(LinkQueue &Q,Node *e) /插入一个指针e到链队列Q中,成为新的队尾指针 QueuePtr p; p=new QNode; p-data=e; p-next=NULL; Q.rear-next=p; Q.rear=p; /EnQueue Node *DeQueue(LinkQueue &Q) /若链队列Q为空,则返回NULL;否则返回指向数据的指针 QN
13、ode *p; Node *e; if(Q.front-next=NULL)return NULL; p=Q.front-next; e=p-data; Q.front-next=p-next; if(Q.rear=p)Q.rear=Q.front; delete p; return e; /DeQueue (3)辅助函数的实现(共7个) ElemType Row_min(Node *node,int row,ElemType &second) /计算路程矩阵行的最小值 if(node-crow0crow1) temp=node-crow0; second=node-crow1; else t
14、emp=node-crow1; second=node-crow0; for(i=2;ik;i+) if(node-crowicrowi; else if(node-crowicrowi; return temp; ElemType Col_min(Node *node,int col,ElemType &second) /计算路程矩阵列的最小值 if(node-c0colc1col) temp=node-c0col; second=node-c1col; else temp=node-c1col; second=node-c0col; for(i=2;ik;i+) if(node-cicol
15、cicol; else if(node-cicolcicol; return temp; ElemType Array_red(Node *node) /归约node所指向的结点的路程矩阵 sum=0; for(i=0;ik;i+) /行归约 temp=Row_min(node,i,temp1); /行归约常数 for(j=0;jk;j+) node-cij-=temp; sum+=temp; /行归约常数累计 for(j=0;jk;j+) /列归约 temp=Col_min(node,j,temp1); /列归约常数 for(i=0;ik;i+) node-cij-=temp; sum+=t
16、emp; return sum; ElemType Edge_sel(Node *node,int &vk,int v1) /计算D,选择搜索分支的边 kl d=0; ElemType *row_value=new ElemTypenode-k; ElemType *col_value=new ElemTypenode-k; for(i=0;ik;i+) /每一行的次小值 Row_min(node,i,row_valuei); for(i=0;ik;i+) /每一列的次小值 Col_min(node,i,col_valuei); for(i=0;ik;i+) /对路程矩阵所有的0元素值 for
17、(j=0;jk;j+) /计算相应的temp值 if(node-cij=0) temp=row_valuei+col_valuei; if(tempd) /求最大的temp值于d d=temp; vk=i; v1=j; /保存相应的行、列号 delete row_value; delete col_value; return d; void Del_rowcol(Node *node,int vk,int v1) /删除路程矩阵的第vk行和第v1列的所有元素 for(i=vk;ik-1;i+) /元素上移 for(j=0;jcij=node-ci+1j; for(j=v1;jk-1;j+) /
18、元素左移 for(i=0;icij=node-cij+1; /元素上移及左移 for(i=vk;ik-1;i+) for(j=v1;jk-1;j+) node-cij=node-ci+1j+1; vk1 /当前行转换为原始行 vk1=node-init_rowvk; /原始行vk1置删除标志 node-row_curvk1=-1; 之后的原始行,其对应的当前行号减1 /vk1 for(i=vk1+1;irow_curi-; /当前列v1转换为原始列 vl1 vl1=node-init_colv1; 置删除标志/原始列vl1 node-col_curvl1=-1; 1 /vl1之后的原始列,其对
19、应的当前列号减 for(i=vl1+1;icol_cur-; for(i=vk;ik;i+) 修改/ vk及其后的当前行的对应原始行号 node-init_rowi=node-init_rowi+1; 修改 /v1及其后的当前列的对应原始行号 for(i=v1;ik;i+) node-init_coli=node-init_coli+1; 1 / 当前矩阵的阶数减 node-k-; void Edge_byp(Node *node,int vk,int vl) /登记回路顶点邻接表,旁路有关的边 / 当前行号转换为原始行号 vk=init_rowvk; vl=init_colvl; 当前列号转
20、换为原始列号 / 登记回路顶点邻接表/ node-advk=vl; k=node-row_curvl; /vl转换为当前行号k l=node-col_curvk; /vk转换为当前列号l if(k=0)&(l=0) /当前行、列号均处于当前矩阵中 node-ckl=MAX_VALUE_OF_TYPE; /旁路相应的边 Node * Initial(ElemType c,int n) /初始化 Node *node=new Node; /分配结点缓冲区 for(i=0;in;i+) /复制路程矩阵的初始数据 for(j=0;jcij=cij; for(i=0;iinit_rowi=i; node
21、-init_coli=i; node-row_cur=i; node-col_cur=i; for(i=0;iadi=-1; node-k=n; return node; /返回结点指针 (4)TSP问题分支限界算法 void TSP(ElemType c100100,int n,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(xnod
22、e,vk,vl); /选择分支方向并计算Dkl znode=new Node; /建立分支结点-z结点(右儿子结点) 结点z结点数据复制给/x *znode=*xnode; znode-cvkvl=MAX_VALUE_OF_TYPE; /旁路结点的边 Array_red(znode); /归约z结点路程矩阵 znode-w=xnode-w+d; /计算z结点的下界 if(znode-ww=Array_red(xnode); /归约y结点路程矩阵 ynode-w+=xnode-w; /计算y结点的下界 if(ynode-k=2) /路程矩阵只剩2阶 if(ynode-c00=0)&(ynode-
23、c11=0) ynode-adynode-init_row0=ynode-init_col0; ynode-adynode-init_row1=ynode-init_col1; else ynode-adynode-init_row0=ynode-init_col1; ynode-adynode-init_row1=ynode-init_col0; /登记最后的两条边 ynode-k=0; if(ynode-wk=0) /更新当前可行解最优值 bound=ynode-w; else delete ynode; /否则剪去y结点 xnode=DeQueue(qbase); /取优先队列首元素 w
24、=xnode-w; /保存最短路线长度 for(i=0;iadi; delete xnode; /释放x结点缓冲区 释放队列节点缓冲区/ while(qbase.front!=qbase.rear) xnode=DeQueue(qbase); delete xnode; coutThe way is:; for(int m=0;mn;m+) coutadm; coutendl; coutThe length of the way is:wendl; (5)主函数的伪码算法 void main() coutn; cout”Please insert the distance between one city and another:”; for(i=0;in;i+) for(j=0;jcij; cout” ”; coutendl; TSP(c, n, ad);/main (6)函数调用关系图 main TSP EnQueue Initial Array_red Edge_sel Edge_byp DeQueue InitQueue Del_rowcol Row_min 5调试分析 (1) 使用分支限界法求解的过程中,将动态地生成很多结点,用结点表来存放动态生成的结点信息。因此必须按路
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1