TSP问题-二进制+DP

题目描述

从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。

输入描述:
城市个数n(1<n≤20,包括北京)
城市间的车票价钱 n行n列的矩阵 m[n][n]
输出描述:
最小车费花销 s

输入
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出
13

求解思路

旅行商TSP问题:若干个城市之间存在一定的路径想连,从起点出发,如何安排合理地行走路径,使得每个城市只走一次,并且最终的总路程最短

本题其特殊之处在于其两两之间均有相互连通,那么最朴素的做法是对剩余的城市全排列,总和最短的那个排列即为所求,这种做法为指数级别复杂度

以s0,s1,s2,s3共4个城市为例:从s0出发经过剩余的三个城市最终回到s0
若第一个去s1,那么问题转变为s1出发,经过剩余若干城市{s2,s3}回到s0的最短路径
若第一个去s2,那么问题转变为s2出发,经过剩余若干城市{s1,s3}回到s0的最短路径
若第一个去s3,那么问题转变为s3出发,经过剩余若干城市{s1,s2}回到s0的最短路径

形式化描述为:
dis[0][1]+dp[1][{2,3}] 首先到s1的距离+剩余子问题距离
dis[0][2]+dp[2][{1,3}] 首先到s2的距离+剩余子问题距离
dis[0][3]+dp[3][{1,2}] 首先到s3的距离+剩余子问题距离
最终问题为上述三种情况取最小值即可

实现细节

本题的思路是子问题,最终动态规划求解出原问题,那么关键之处在于表达城市集合
除去起始点s0外,剩下的n-1个城市,可以采取n-1个二进制位表示(类似bitmap思想)
初始化时,dp[k][0]=arr[k][0] 即从城市k经过0个城市到达终点s0的距离(相当于集合城市都没经过)
按照经过城市集合数量由少=》多的递推方向进行

以计算城市i为例子:
dp[i][j]=min(dp[i][j],arr[i][k]+dp[k][other]) 其中k= 1=>n-1
即i的下一个城市k可能为剩余城市群集合J中的一个
other即表示城市群集合j减去城市k之后的二进制表示
other=j^(1<<(n-1)) 异或操作使得对应位置1=>0

代码实现

#include<iostream>
#include<vector>
using namespace std;
static const int MAX=1000000000;
int main()
{
    int n;
    cin>>n;
    vector<vector<int>> arr(n,vector<int>(n));
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>arr[i][j];

    int all=1<<(n-1);//[0=>2的n-1次方-1]  
    //若某位为0表示集合中不包含该位对应的城市
    //若某位为1表示结合中包含该位对应的城市
    //即 采取一个数 来表示城市集合
    //注意这里采取n-1 即城市0始终不在待考虑城市群中
    vector<vector<int>> dp(n,vector<int>(all,MAX));
    //dp[i][j]表示从i出发 经过若干城市(j集合表示的城市群)回到起点的最短开销  初始化为无穷大
    //开始迭代计算  首先从一个城市出发 一定是小城市群=》大城市群 计算(后面利用前面的结果)
   for(int j=0;j<all;j++) 
    for(int i=0;i<n;i++)
        {
            if(j==0) //表示不经过城市群 直接回到0号城市
            {
                dp[i][0]=arr[i][0];
            }
            else
            {
               for(int k=1;k<n;k++) //k表示首先经过k中转更快
               {
                   //那么城市群集合j中首先去除k城市
                   int other=(1<<(k-1));//00001000  任何位和1异或变为相反数 和0异或不变
                   //相当于在j中 去除k对应的位(置为0)
                   //即i首先去城市k 然后经过other城市回到起点(相当于枚举i下一个可能的城市)
                   if((other&j)>0) 
                        dp[i][j]=min(dp[i][j],arr[i][k]+dp[k][other^j]);
                   //这里dp[k][other]一定计算过 因为other一定比j小
               }
            } 
        }
    cout<<dp[0][all-1]<<endl; //最终所求 从0号城市出发 经过剩余的城市 回到起点(0)的dp值
    
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页