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

来源:互联网 发布:淘宝评价非欧什么意思 编辑:程序博客网 时间:2024/05/17 09:13

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;    }       } 
0 0
原创粉丝点击