算法导论——双调欧几里得旅行商问题

xiaoxiao2021-02-28  85

     欧几里得旅行商问题是对平面上给定的n个点确定一条连接各点的最短闭合旅程的问题。

     如图(a)给出了一个7个点问题的解。这个问题的一般形式是NP完全的,故其解需要多于多项式的时间。

    J.L. Bentley 建议通过只考虑双调旅程(bitonic tour)来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。下图(b)显示了同样的7个点的最短双调路线。在这种情况下,多项式的算法是可能的。事实上,存在确定的最优双调路线的O(n*n)时间的算法。

       

   此处开始出干货:

   1.此处需要注意理解题意:题目限定条件是图没有重复的点,此处所谓没有重复的点是指点的X(横坐标)没有相同的,形象一点就是在一根竖直的线上绝不会出现两个点。(这一点是理解题目的关键,注意这一点其实就不难了);

   2.总的来看,该题的思路是分为上下两条路径,再进一步的情况是最末相邻的两个点是否在一条路径上。(这也就是讨论j<i-1和j=i-1的两种情况)

   

  3.先附上算法导论的详细讲解:

    

     详细注解:

           1.我首先需要对于i>=j的情况进行分析,因为i<j的情况与此是对称的。

           2.动态规划的定义是找出最优子问题。

            (1)当i-1>j时:d[i][j]=d[i-1][j]+|i,i-1|;

            (2)当i-1=j时:d[i][j]=d[i][k]+d[k][j];

           对于上面两个式子的说明:

           d[i][j]:含义是第一个点到第i点和第j个点的距离之和,可以理解成|1,i|+|1,j|;

           |i,j|:代表第i和第j个点的直线距离;

           3.其中涉及到一次枚举:当i-1=j的时候,i-1与i点(这是两个相邻点,所谓的相邻点即是指横坐标相差最小的点)不在一个路径             上,所以需要找到一个与i距离最近的且在同一条路径上的点K(此处所谓的距离最近的点即是在这两点之间没有其他的点存在)。

           所以可以很自然的得到d[k][i]=|k,i|。一开始所说的需要枚举的地方就是最小的Min(d[k][j]+|k,i|)或者

           Min(d[k][i-1]+|k,i|),因为i-1=j;这一点也就是算法导论答案中的第四点;其中有些显而易见的变换:d[i][j]=d[j][i](原            因是双调欧几里得图时对称的)。

      一份POJ提交的代码:该代码为POJ上略微修改不算原创,无法确认版主,侵权立删

 

#include <iostream> #include <cmath> #include <iomanip> using namespace std; const int n = 7;//点的数目 const int MaxVal = 1000000; const int MaxLen = 210; struct tagPoint{     double x,y; }; ///计算点i和点j之间的直线距离 double distance(tagPoint *points,int i,int j) {     return sqrt((points[i].x - points[j].x) * (points[i].x - points[j].x) +         (points[i].y - points[j].y) * (points[i].y - points[j].y)); } double DP(tagPoint *points,int n) {     double b[MaxLen][MaxLen];//记录最短路径的长度     //计算所有情况下的b[i][j],1 <= i <= j     //初始化     b[1][2] = distance(points,1,2);     for (int j = 3;j <= n;++j)     {         ///i < j-1         for (int i = 1;i <= j - 2;++i)         {             b[i][j] = b[i][j - 1] + distance(points,j - 1,j);         }         ///i = j - 1,b[i][j] = min(b[k][j - 1] + distance(k,j));         ///也可以写成  b[i][j]=min( b[i][k]+distance(k,j))         b[j - 1][j] = MaxVal;         for (int k = 1;k <= j - 2;++k)         {             double temp = b[k][j - 1] + distance(points,k,j);             if (temp < b[j - 1][j])             {                 b[j - 1][j] = temp;             }         }     }     b[n][n] = b[n - 1][n] + distance(points,n - 1,n);     return b[n][n]; } int main() {     int NUM;     while(cin >> NUM)     {         tagPoint *points = new tagPoint[NUM + 1];         for (int i = 1;i <= NUM;++i)         {             cin >> points[i].x;             cin >> points[i].y;         }         double minDis = DP(points,NUM);         //设置输出格式:精确到小数点后2位         cout.setf(ios::fixed);         cout << setprecision(2) << minDis << endl;     } }

转载请注明原文地址: https://www.6miu.com/read-44875.html

最新回复(0)