算法分析 | 回溯法 | 旅行商问题

一.问题分析

1.问题描述:一个联通无向图中,求最短路径回路.也就是求出一个最佳序列,并且终点和起点有直接路径.

2.问题分析:

     ① 约束条件:因为存在着两个结点不直接相连的情况,有些序列一开始就不可能出现.约束函数存在

记录连接情况的二维数组 T[ t - 1 ][ i ] !=  \infty           // t-1表示上一个结点; i表示全部剩余节点

 

      ②限界函数:现有距离+上一站到某个分支的距离优于现有最优值.  当最优解是最小值时,它的初值应该设为 \infty 

当前距离 cn + 新增距离T[ x[t-1] ][ x[i] ] < 一次递归最优解 bestn      //如果误写成T[ t - 1 ][ i ],意味着解序列是默认的{0,1,2,3...}

 

      ③具有递归问题中的元素全排列性质, 存在代码结构:        一般来说,还要在Backtrack( t + 1 )前存储当前值,以便回溯

Backtrack( t )

{

...

  for (int i = t; i < N; i++)

  {

      swap(i,t);            //a[i]表示全部剩余元素, t表示子问题的队首a[t]

      Backtrack(t+1);

      swap(i,t);

  }

}

 

 

二.代码实现

分为三部分

1.全局变量定义. 

      ①在实际中,"\infty" 用C++自带的常量INT_MAX表示.

      ②顺便一提,二维vector输出行数:T.size();  输出每一行列数T[i].size();

2.递归函数

      ①使用首位为0的方式计算,A[0]表示起点,从A[1]位开始递归

3.初始化/调用递归函数/打印结果   3 in 1

//TSP is short of "Traveling Salesman Problem" ,旅行商问题
 
const int M4 = 5;
const int INF = INT_MAX;
vector<vector<int>>T1=        //一个5*5的二维数组
{
{INF,3,INF,8,9},
{3,INF,3,10,5},
{INF,3,INF,4,3},
{8,10,4,INF,20},
{9,5,3,20,INF}
};

int cn3;	                                //记录当前值									  
vector<int> cx3(M4, 0);			    	//记录当前序列

int bestn3;					//记录一次递归结束后最优值
vector<int> bestx3(M4, 0);		        //记录一次递归结束后最优策略


void TSPbacktrack(int t)  //默认起点是0,t从 1 开始求解
{
    //先写终止条件
     if (t >= M4                      &&          //遍历队尾元素,到达叶结点
     T1[ 0 ][ cx3[M4-1] ]!=INF        &&          //cx3[]已得出一个解序列,如果结点 0 (起点)和cx3[n-1](终点)相通
     cn3 + T1[ 0 ][ cx3[M4 - 1] ]<bestn3)
        {
            bestx3 = cx3;
            bestn3 = cn3 + T1[ cx3[M4 - 1] ][0];      //最后加上终点->起点距离 
        }
    else
    {
		for (int i = t; i < M4; i++)
        {
            if (T1[ cx3[ t - 1 ] ][ cx3[ i ] ] != INF      &&                         //当前分支中某结点与上一站相通 且
                cn3 + T1[ cx3[ t - 1 ] ][ cx3[ i ] ] < bestn3)             //现有距离+上一站到某个分支的距离优于现有最优值
            {
                swap(cx3[i], cx3[t]);                                   //将选中的该结点与子问题的队首交换
                cn3 += T1[ cx3[ t - 1] ][ cx3[ t ] ];                       //已选中该结点,更新当前值
                TSPbacktrack(t + 1);                                  //递归求解N-1子问题
                cn3 -= T1[cx3[ t - 1 ] ][ cx3[ t ] ];                       //递归完该结点,恢复当前值
/*①*/           swap(cx3[i], cx3[t]);                                   //回溯          
            }
        }
	}
}



void TSP()
{
    //初始化
    cn3 = 0;        //当前初始值=0
    /*②*/bestn3 = INF;   //求最小值,一次递归最优值的初始值=无穷大
    for (int i = 0; i < M4; i++)
    {
        cx3[i] = i;     //默认序列是0,1,2,3....
    }

    //运算
    TSPbacktrack(1);//默认起点是点0,从点1开始

    //输出结果
    cout << "最优旅行顺序:		";
    for (int i = 0; i < M4; i++)
    {
        cout << bestx3[i] << "-";
    }cout << "0"; 
    cout << endl;
    cout << "最短里程:		" << bestn3;
}

 

三.BUG分析

主要集中在"解序列"和"默认序列"的差异上

Bug①处:原错误语句:

swap(i, t);                     

修正后:

swap(x[ i ],x[ t ]);                          //交换的是序列

 

Bug②处:原错误语句

bestn3 = 0;                                 //当最优值是一个最小值时,它的初值应该=\infty               

posted @ 2020-02-17 17:54  心碎人俱乐部  阅读(201)  评论(0)    收藏  举报