C++ 动态规划求解TSP(旅行商问题)

动态规划“四部曲”

  1. 问题结构分析: 给出问题表示,明确原始问题。

  2. 递推关系建立: 分析最优(子)结构,构造递推公式。

  3. 确定计算顺序: 确定计算顺序,依次求解问题。

  4. 最优方案追踪: 记录决策过程,输出最优方案。

TSP问题介绍

旅行商问题,即TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个人要拜访n个城市,从当前所在城市出发,经过且仅经过一次所有城市,回到出发的城市。选择一条路径使得其路程为所有路径中的最小值。
TSP问题模型

使用动态规划分析TSP

问题结构分析

给出问题表示

D[location , V]:从当前所在城市location经过V‘集合(剩余未访问城市的集合)的最短路径。

明确原始问题

D[i , V]:从当前所在城市location经过V‘集合(剩余未访问城市的集合)的最短路径。

递推关系建立

分析最优(子)结构

D[i , V] = Cik + D[k , V - {k} ]

构造递推公式

当V‘为空集时,此时所有顶点均被访问过,直接回到起始顶点即可。(a代表起始点)

{ C i a   , V = { } 且 i ≠ a m i n { C i k + D [ k , V − k ] } , V ≠ { } \left\{ \begin{array}{c} Cia~ , V=\{\}且i≠a \\ min \{{ Cik + D[k , V - {k} ] \}} ,V≠\{\} \end{array}\right. {Cia ,V={}i=amin{Cik+D[k,Vk]}V={}

确定计算顺序

✅:可以直接求得。
❌:不存在该路径。
D[2,{1,3}] = min{ C21+D[1,{}] , C23 + D[3,{}] }

{}{1}{2}{1,2}{3}{1,3}{2,3}{1,2,3}
i=1
i=2D[2,{1,3}]
i=3

由表格发现,D[2,{1,3}]依赖其左边部分的子问题,确定计算顺序由左到右。从不同顶点出发的最优解在表中的位置不同。

最优方案追踪

创建一个Rec数组用于存放决策,记录前驱节点。

{}{1}{2}{1,2}{3}{1,3}{2,3}{1,2,3}
i=1
i=2
i=3

C++代码

创建二维数组求解、追踪最优解,对于n顶点的图,需要创建一个n*2n的二维数组,对于前一部分中列的排序为什么是{}、{1}、{2}、{1,2}、{3}、{1,3}、{2,3}、{1,2,3},大家可能存在疑问,实际上这是为了方便编写代码,使用数字的二进制表示集合中存在的元素,例如000(第0列)表示空集,111(第7列)表示{1,2,3}。

#include<iostream>
#include<cmath>
using namespace std;
#define N 9999
//TSP问题求解函数
void TSP(int n,int** graph,int location){
    //构建二维数组[i,2^n],j模拟集合个数
    int **D = new int*[n+1];  //建行0~n,为方便理解和表示,第0行未使用
    int **Rec = new int*[n+1];  //建立Rec
    for(int i=1;i<=n;i++){
    //建列表示集合
        D[i] = new int [(int)pow(2,n)]; 
        Rec[i] = new int [(int)pow(2,n)];
    }
    //初始化D、Rec
    for(int i=1;i<=n;i++){
        D[i][0]=graph[i][location]; //D[i,{}]
        Rec[i][0]= -1;
        for(int j=1;j<=(int)pow(2,n)-1;j++){
            D[i][j]=N;
            Rec[i][j] = -1;
        }
    }
    //动态规划
    for(int j=1;j<=(int)pow(2,n)-1;j++){    //对每一列求解
        for(int i=1;i<=n;i++){  //每一行找最短路径
            int min = N;
            if(((int)pow(2,i-1) & j) == 0){   //顶点集不能包含i
                int length = N;
                for(int k=1;k<=n;k++){
				    if((int)pow(2,k-1) & j ){ //若顶点在集合中
                        length = graph[i][k] + D[k][j-(int)pow(2,k-1)];
                        if(length < min){
                            min = length;
                            D[i][j] = min;
                            Rec[i][j] = k;//局部最优决策
                        }
                    }
                }
            }
        }
    }
    cout<<"最短长度:"<<D[location][(int)pow(2,n)-1-(int)pow(2,location-1)]<<endl;//最短路径长度
	cout<<"最短路径为:"<<location;
    int row = location;
    int column = (int)pow(2,n)-1-(int)pow(2,row-1);
    while(column > 0){
        cout<< "->"<<Rec[row][column];
        row = Rec[row][column];
        column -= (int)pow(2,row-1);
    }
	cout<<"->"<<location<<endl;
}

int main(){
    cout<<"旅行家需要游历多少个城市?:"<<endl;
    int n;
    cin>>n;
    //建立二维数组模拟邻接矩阵
    int **graph=new int* [n+1];
    for(int i=1;i<=n;i++)
        graph[i] = new int[n+1];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cout<<"输入邻接矩阵graph["<<i<<"]["<<j<<"]的权:"<<endl;
            cin>>graph[i][j];
        }
    }
    cout<<"旅行家现在在第几个城市?"<<endl;
    int location;
    cin>>location;
    TSP(n,graph,location);   //TSP求解
    return 0;
} 

时间复杂度分析

如代码所示,求解TSP问题时间花费最主要在n*2n的二维数组的操作上,故时间复杂度为O(n*2n),时间复杂度随n的增大呈指数爆炸增长。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值