动态规划经典问题--TSP问题

Travelling Salesman Problem

旅行商问题,即TSP问题(Travelling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
旅行商问题是图论中最著名的问题之一,即“已给一个n个点的完全图,每条边都有一个长度,求总长度最短的经过每个顶点正好一次的封闭回路”。该问题通常被认为是一个NP完全问题。时间复杂度为O(n!)。因此,通常n的值不是很大。

因此我们如何求其近似解(非多项式时间算法)呢?这里使用动态规划

  1. 假定我们从城市1出发,经过了一些地方,并到达了城市j。毋庸置疑,我们需要记录的信息有当前的城市j。同时我们还需要记录已经走过的城市的集合。同理,使用S记录未走过的城市的集合也可以的,且运算方便。
  2. 于是我们可以得出状态转移方程 go(S,init)=min{go(Si,i)+dp[i][init]}sS go(s,init)表示从init点开始,要经过s集合中的所有点的距离
  3. 因为是NP问题,所以时间复杂度通常比较大。使用dis[s][init]用来去重,初始化为-1,如果已经计算过init—>S(递归形式),则直接返回即可

例: Sicily1000. Traveling Salesman Problem
题目大意:有编号1到N的N个城市,问从1号城市出发,遍历完所有的城市并最后停留在N号城市的最短路径长度。N<=20
那么如何有效地储存当前还未走过的城市集合S呢?使用一个整形即可。每个整形可以表示32位,即32个城市,用1代表当前未去,0代表当前已经访问过的点。因N<=20,所以S<=1048576.

代码如下:*

#include<iostream>
#include<cmath> 
#include<iomanip>
using namespace std;
int s;
int N;//点的个数 
int init_point;
double x[20];
double y[20];
double dp[20][20];//两个城市的距离 
double dis[1048577][20];//2^20=1048576 表示出发点到S集合是否已经访问过 
double go(int s,int init)
{
    if(dis[s][init]!=-1) return dis[s][init];//去重 
    if(s==(1<<(N-1))) return dp[N-1][init];//只有最后一个点返回 
    double minlen=100000;
    for(int i=0;i<N-1;i++)//只能在1到n-2点中查找 
    {
        if(s&(1<<i))//如果i点在s中且不为发出点 
        {
            if(go(s&(~(1<<i)),i)+dp[i][init]<minlen)
                minlen=go(s&(~(1<<i)),i)+dp[i][init];
        } 
    } 
    return dis[s][init]=minlen;
}
int main()
{
    int T;
    cin>>T;
    while(T--)//测试样例数 
    {
        cin>>N;
        for(int i=0;i<N;i++)
            cin>>x[i]>>y[i];//读入城市的坐标 
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
            {
                dp[i][j]=sqrt(pow((x[i]-x[j]),2)+pow((y[i]-y[j]),2));
                //计算两个城市的距离 
            }   

        for(int i=0;i<pow(2,N);i++)
            for(int j=0;j<N;j++)
                dis[i][j]=-1;//去重数组初始化 
        init_point=0;
        s=0; 
        for(int i=1;i<N;i++)
            s=s|(1<<i);//从1开始,保证初始点没有在S里面 
        double distance=go(s,init_point);
        cout<<fixed<<setprecision(2)<<distance<<endl;

    }       
} 

你可能感兴趣的:(动态规划)