TSP问题之回溯算法

来源:互联网 发布:sql注入检测工具 编辑:程序博客网 时间:2024/05/29 17:23
TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市,要求路径的总和最小。考虑用回溯算法求解。w(vi,vj)表示城市i与j间的直接距离,w(vi,vj)=∞表示城市i与j间无直接路径。将图中n个顶点编号为1,2,…,n,以顶点1为起点,旅行回路描述为1,x1,x2,..,xn,1,其中x1,x2,..,xn为顶点2,3,4,…,n的1个排列,因此解空间大小为(n-1)!。
有两个剪枝约束:
1.如果当前正在考虑的顶点j与当前路径中的末端结点i没有边相连,即w[i,j]=∞,则不必搜索j所在分支。


例如,当前已有的部分路径为<1,2,?,?>,根据路径组成规则,下一步可考虑将顶点3、4加入到部分路径中。但是顶点2与4间无边,w(2,4)=∞,因此可以不必考虑顶点4所在分支。
2.令到第i层结点为止,构造的部分解路径为<1,x[2],x[3],…,x[i-1], x[i],?,?,?>,该路径的权值总和为


假设已经知道直到第i-1层的部分解<1, x[2],x[3],…,x[i-1],?,?,?>,从第i-1层结点选择顶点x[i],并向该分支往下搜索的界限函数为B(i)=cw(i-1)+w(x[i-1],x[i]),如果B(i)≥bestw则停止搜索x[i]分支及其下面的层,其中bestw代表到目前为止在前面的搜索中,从其它已经搜索过的路径中找到的最佳完整回路的总长度。


例如,该问题最优解为<1,3,2,4,1>和<1,4,2,3,1>,对应的bestw=25。假设搜索另一分支<1,3,4,?>,当前路径对应的结点为I,长度=6+20=26>bestw=25,则结点I之下的路径被舍弃。


下面的代码输入城市的个数n和边数m,接着输入每天边的起点、终点和长度,计算从第一个城市开始的TSP问题的最优解。

#include<cstring>  #include<iostream>  #define INF 10000  using namespace std;  int n,m;//n个点m条边   int bestans=INF,ans=0;//最优解和当前解   int bestroad[100],road[100];//最佳路径和当前路径   int graph[100][100];//图的矩阵   bool vis[100];//访问标记      void backtrack(int i)  {      if(i>n)              {          if(graph[road[n]][1]!=INF&&(ans+graph[road[n]][1])<bestans)          {                  bestans=ans+graph[road[n]][1];              for(int j=1;j<=n;j++) bestroad[j]=road[j];          }      }      else         {          for(int j=1;j<=n;j++)          {              if(graph[road[i-1]][j]!=INF&&ans+graph[road[i-1]][j]<bestans&&!vis[j])              {                  road[i]=j;                          ans+=graph[road[i-1]][j];                  vis[j]=1;                  backtrack(i+1);                  //改回辅助的全局变量                   ans-=graph[road[i-1]][j];                  vis[j]=0;              }          }      }  }    int main()  {      memset(graph,INF,sizeof(graph));      cin>>n>>m;      for(int i=1;i<=m;i++)      {          int a,b;          cin>>a>>b;          cin>>graph[a][b];          graph[b][a]=graph[a][b];      }       vis[1]=1;      road[1]=1;      //假设是从1开始       backtrack(2);      cout<<bestans<<endl;      for(int i=1;i<=n;i++) cout<<bestroad[i]<<" ";      cout<<1<<endl;  }


0 0
原创粉丝点击