图论与代数实验报告旅行售货员问题(TSP)某售货员要到若干城市去推销商品,一直各城市之间的路程,他要选定一条从驻地出发,经过每个城市一遍,最后回到住地的路线,使总的路程最短。
我用分支限界法解决问题。
1、分支限界法基本思想分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。
活结点一旦成为扩展结点,就一次性产生其所有儿子结点。
在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。
此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。
这个过程一直持续到找到所需的解或活结点表为空时为止。
2、常见的两种分支限界法(1)队列式(FIFO)分支限界法按照队列先进先出(FIFO)原则选取下一个节点为扩展节点。
(2)优先队列式分支限界法按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。
最大优先队列:使用最大堆,体现最大效益优先。
最小优先队列:使用最小堆,体现最小费用优先。
该问题是一个NP完全问题,有(n-1)!条可选路线。
路线是一个带权图。
图中各边的费用(权)为正数。
图的一条周游路线是包括V中的每个顶点在内的一条回路。
周游路线的费用是这条路线上所有边的费用之和。
旅行售货员问题的解空间可以组织成一棵树,从树的根结点到任一叶结点的路径定义了图的一条周游路线。
旅行售货员问题要在图G 中找出费用最小的周游路线。
即:设G(V,E)是一带权有向图,V={1,2,…n },其耗费矩阵C=(ci,j),当时, 记ci,j=无穷大且ci,j=无穷大.问如何选择周游路线使耗费最小?算法思路:设周游路线从结点1开始,解为等长数组X=(1,x2,...xn)则解空间树为排列树,在树中做广度优先搜索。
约束条件: xi 不等于xj ,i 不等于j;目标函数:解向量对应的边权之和∑Cij目标函数限界初值:U=无穷大C=算法描述:①算法开始时创建一个最小堆,用于表示活结点优先队列②堆中每个结点的子树费用的下界lcost 值是优先队列的优先级。
③接着算法计算出图中每个顶点的最小费用出边并用minout 记录。
④如果所给的有向图中某个顶点没有出边,则该图不可能有回路,算法即告结束。
⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎣⎡∞∞∞∞201042056105304630⑤如果每个顶点都有出边,则根据计算出的minout作算法初始化。
1、float row_min(NODE *node,int row,float &second)功能:返回由指针node所指向节点费用矩阵中第row行最小值,并把次小值回送与引用变量second中2、float col_min(NODE *node,int col,float &second)功能:返回由指针node所指向节点费用矩阵中第col列最小值,并把次小值回送与引用变量second中3、float array_red(NODE *node)功能:归约指针node指向的节点的费用矩阵返回值为归约常数4、float edge_sel (NODE *node,int &vk,int &vl)功能:计算d并且搜索分支的边返回dkl的值kl5、void del_rowcol(NODE *node,int vk,int vl)功能:删除费用矩阵当前V行第l V列的所有元素k6、void edge_byp(NODE *node,int vk, int vl)功能:把vk vl表示的边登记到顶点邻接表并旁路矩阵中有关的边7、NODE *initial(float c[20][20],int n)功能:初始化函数8、void insert(HEAP *&heap,int &n_heap,HEAP z)功能:向最小堆栈插入结点函数9、HEAP delete_min(HEAP *&heap,int &n_heap)功能:删除堆栈头结点函数分支限界法求解TSP问题的程序#include <iostream>#include <fstream>#include <string>#include <cmath>using namespace std;//定义相关结构体typedef struct node_data //结点数据结构{float c[20][20]; /*费用矩阵*/int row_init[20]; /*费用矩阵的当前行映射为原始行*/ int col_init[20]; /*费用矩阵的当前列映射为原始列*/int row_cur[20]; /*费用矩阵的原始行映射为当前行*/ int col_cur[20]; /*费用矩阵的原始列映射为当前列*/ int ad[20]; /*回路顶点邻接表*/int k; /*当前费用矩阵的阶*/float w; /*结点的下界*/}NODE;typedef struct /*堆结构数据*/{NODE *p; /*指向结点元素的指针*/float w; /*所指向结点的下界,堆元素的关键字*/}HEAP;//主要函数申明部分float row_min(NODE *node,int row,float &second);float col_min(NODE *node,int col,float &second);float array_red(NODE *node);float edge_sel (NODE *node,int &vk,int &vl);void del_rowcol(NODE *node,int vk,int vl);void edge_byp(NODE *node,int vk, int vl);NODE *initial(float c[20][20],int n);void insert(HEAP *&heap,int &n_heap,HEAP z);HEAP delete_min(HEAP *&heap,int &n_heap);//主函数开始int main(){int n=20;int ad[20],path[20];float c[20][20];float max_distance, min_distance;string filename="distance.txt";ifstream sensor;sensor.open(filename.c_str()); //打开文件读取距离数据信息if(sensor.fail()){cout<<"Error opening input file\n";}else{sensor>>max_distance>>min_distance; //没有到达文件末尾时循环读取数据while(!sensor.eof()){for(int i=0;i<20;i++){for(int j=0;j<20;j++){sensor>>c[i][j];}c[i][i]=max_distance+1;}}} //读取距离信息结束int i,j,vk,vl;float d,w=0;NODE *xnode;NODE *ynode;NODE *znode;int n_heap=0;HEAP *heap=new HEAP[50*30]; HEAP x,y,z;xnode=initial(c,n);xnode->w=array_red(xnode);while(xnode->k!=0){d=edge_sel(xnode,vk,vl);znode=new NODE;*znode =*xnode;znode->c[vk][vl]=max_distance+1;array_red(znode);znode->w=xnode->w+d;z.w=znode->w;z.p=znode;insert(heap,n_heap,z);ynode=new NODE;*ynode=*xnode;edge_byp(ynode,vk,vl);del_rowcol(ynode,vk,vl);ynode->w=array_red(ynode);ynode->w+=xnode->w;y.w=ynode->w;y.p=ynode;if(ynode->k==2){if(ynode->c[0][0]==0){ynode->ad[ynode->row_init[0]]=ynode->col_init[1];ynode->ad[ynode->row_init[1]]=ynode->col_init[0];}else{ynode->ad[ynode->row_init[0]]=ynode->col_init[0];ynode->ad[ynode->row_init[1]]=ynode->col_init[1];}ynode->k=0;}insert (heap,n_heap,y);delete xnode;x=delete_min(heap,n_heap);xnode=x.p;}w=xnode->w;for(i=0;i<n;i++) //保存邻接表ad[i]=xnode->ad[i];delete xnode;for (i=0;i<n_heap;i++) //释放结点数据空间{delete heap->p;heap--;}heap++;delete []heap; //释放堆空间int t=0,k=1; //将邻接表转化为遍历城市的顺序列表,保存在path[]中path[0]=0;while(k<n){j=t;t=ad[j];path[k]=t;k++;}cout<<"the result of the path:"<<endl; //输出最优遍历城市顺序的结果,转化为A、B的形式for(j=0;j<20;j++){cout<<char(path[j]+65)<<' ';}cout<<endl<<"the total length is: "<<w<<endl; //输出最短路程的值return 0;} //主函数结束/**********************************函数定义部分***********************************///返回由指针node所指向节点费用矩阵中第r ow行最小值,并把次小值回送与引用变量second中float row_min(NODE *node,int row,float &second){float temp;int i;if (node->c[row][0],node->c[row][1]){temp =node->c[row][0]; second=node->c[row][1];}else{temp=node->c[row][1]; second=node->c[row][0];}for (i=2;i<node->k;i++){if(node->c[row][i]<temp){second=temp;temp=node->c[row][i];}else if(node->c[row][i]<second)second=node->c[row][i];}return temp;}//返回由指针node所指向节点费用矩阵中第col列最小值,并把次小值回送与引用变量second中float col_min(NODE *node,int col, float &second){float temp;int i;if (node->c[0][col],node->c[1][col]){temp =node->c[0][col]; second=node->c[1][col];}else{temp=node->c[1][col]; second=node->c[0][col];}for (i=2;i<node->k;i++){if (node->c[i][col]<temp ){second =temp; temp =node->c[i][col];}else if (node->c[i][col]<second)second=node->c[i][col];}return temp;}//归约指针node指向的节点的费用矩阵返回值为归约常数float array_red(NODE *node){int i,j;float temp,temp1,sum=0;for(i=0;i<node->k;i++){temp=row_min(node,i,temp1);for (j=0;j<node->k;j++)node->c[i][j] -=temp;sum +=temp;}for(j=0;j<node->k;j++){temp=col_min(node,j,temp1);for (i=0;i<node->k;i++)node->c[i][j] -=temp;sum +=temp;}return sum;}//计算dkl 并且搜索分支的边返回dkl的值float edge_sel(NODE *node,int &vk,int &vl) {int i,j;float temp,d=0;float *row_value=new float[node->k]; float *col_value=new float[node->k];for (i=0;i<node->k;i++)row_min(node,i,row_value[i]);for (i=0;i<node->k;i++)col_min(node,i,col_value[i]);for (i=0;i<node->k;i++){for(j=0;j<node->k;j++){if (node->c[i][j]==0){temp=row_value[i]+col_value[j];if(temp>d){d=temp; vk=i;vl=j;}}}}delete row_value;delete col_value;return d;}//del_rowcol(NODE *node,int vk,int vl)删除费用矩阵当前vk行第vl列的所有元素void del_rowcol(NODE *node,int vk,int vl) {int i,j,vk1,vl1;for (i=vk;i<node->k-1;i++)for (j=0;j<vl;j++)node->c[i][j]=node->c[i+1][j];for (j=vl;j<node->k-1;j++)for (i=0;i<vk;i++)node->c[i][j]=node->c[i][j+1];for (i=vk;i<node->k-1;i++)for (j=vl;j<node->k-1;j++)node->c[i][j]=node->c[i+1][j+1];vk1=node->row_init[vk];node->row_cur[vk1]=-1;for (i=vk1+1;i<20;i++)node->row_cur[i]--;//vl1=node->col_init[vl];node->col_cur[vl1]=-1;for (i=vl1+1;i<20;i++)node->col_cur[i]--;for (i=vk;i<node->k-1;i++)node->row_init[i]=node->row_init[i+1];for(i=vl;i<node->k-1;i++)node->col_init[i]=node->col_init[i+1];node->k--;}//把vk vl表示的边登记到顶点邻接表并旁路矩阵中有关的边void edge_byp(NODE *node,int vk, int vl){int i,j,k,l;vk=node->row_init[vk];vl=node->col_init[vl];node->ad[vk]=vl;for (i=0;i<20;i++){j=i;while (node->ad[j]!=-1)j=node->ad[j];if(i!=j){l=node->row_cur[j];k=node->col_cur[i];if((k>0)&&(l>0))node->c[l][k]=100; //以后要改进的地方100是个特殊值}}}//初始化函数NODE *initial(float c[20][20],int n){int i,j;NODE *node=new NODE;for (i=0;i<n;i++)for (j=0;j<n;j++)node->c[i][j]=c[i][j];for (i=0;i<n;i++){node->row_init[i]=i;node->col_init[i]=i;node->row_cur[i]=i;node->col_cur[i]=i;}for (i=0;i<n;i++)node->ad[i]=-1;return node;}//向最小堆栈插入结点函数void insert(HEAP *&heap,int &n_heap,HEAP z) {HEAP *temp;int k=n_heap;if(k==0){*heap=z;}else{temp=heap;while(heap->w<z.w&&k>0){*(heap+1)=*heap;k--;if(k>0) heap--;}if(k>0) heap++;heap=temp+1;}n_heap++;}//删除堆栈头结点函数HEAP delete_min(HEAP *&heap,int &n_heap) {HEAP temp;temp=(*heap);heap--;n_heap--;return temp;}。