• 算法导论151双调欧几里得旅行商问题 & 152整齐打印


    CLRS 15-1 双调欧几里得旅行商问题

    欧几里得旅行商问题是对平面上给定的n个点确定一条连接各点的最短闭合旅程的问题。如图(a)给出了一个7个点问题的解。这个问题的一般形式是NP完全的,故其解需要多于多项式的时间。
    J. L. Bentley建议通过只考虑双调旅程来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。下图(b)显示了同样的7个点的最短双调路线。在这种情况下,多项式的算法是可能的。事实上,存在确定的最优双调路线的O(n2)时间的算法。
    描述一个确定最优双调路线的O(n2)时间的算法。可以假设任何两点的x坐标都不相同。(提示:从左到右扫描,保持路线两部分的最优概率)。

           a)最短闭合路线,长度大约是24.89。这个路线不是双调的。                                  b)相同点的集合上的最短双调闭合路线。长度大约是25.58

    解题思路:

    1.题目所求的结果就是最左端点到最右端点的两条线路,对于这两条线路,线路上的点的x坐标是递增的(第i个点一定比i-1个点的x坐标大)

    2.从左端点开始,有两条线路出发,用d(i, k)表示两条线路分别到达i点和k点的距离之后,这里指的是最短距离之和,两条线路无相同点(除去起点和终点)。在这里,由于两条线在意义上是等价的,因而我们规定i<=k,即一条线路总是领先着。

    3.从2可以看出,d(n, n)即为所求。

    对于d(i, k),我们可以如下分析:

    1)当k < i-1时,有

        d(i, k) = d(i-1, k) + |Pi-1Pi|,这里表示d(i, k)必然包含线段|Pi-1Pi| (点Pi-1和点Pi之间的距离)

    2)当k = i-1时,有

        d(i, k) = d(i-1, u) + |PuPi|,其中1 <= u < i-1,这里遍历u的值,寻找最短距离

    3)当k = i时,有

        d(i, k) = d(i-1, u) + |Pi-1Pi| + |PuPi|,其中1 <= u < i-1

    对于下面的算法,为了在这省略排序算法(时间复杂度为n*lgn),输入必须按照x坐标由小到大进行,在gcc下用

    gcc travel.c -lm

    命令编译,记得加上-lm参数。

    看起来,这个算法好像有三层for循环,时间复杂度为O(n3),但是我们发现:

    第41行的for (u = 0; u < i-1; u++)只在k == i-1时才执行,并不是在第34行的循环体中每次执行。

    第51行的for循环只在k == i并且i == rows-1的情况下执行,

    因而整体的时间复杂度为O(n2)

     1 #include <stdio.h>
     2 #include <math.h>
     3 #include <float.h>
     4 #include <stdlib.h>
     5 
     6 //*((int*)array + n*i + j); 
     7 //typedef int array[2];
     8 
     9 //坐标
    10 typedef struct point_t
    11 {
    12   int x;
    13   int y;
    14 } point;
    15 
    16 //计算两点之间的距离
    17 double distance(point p1, point p2)
    18 {
    19   return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
    20 }
    21 
    22 double process(point* a, const int rows)
    23 {
    24   double d[rows][rows];
    25   d[0][0] = 0.0;
    26   d[1][0] = distance(a[1], a[0]);
    27 
    28   int i;
    29   int k;
    30   int u;
    31   //按照对d(i,k)的分析写循环
    32   for (i = 2; i < rows; i++)
    33   {
    34     for (k = 0; k <= i; k++)
    35     {
    36       if (k < i-1)
    37         d[i][k] = d[i-1][k] + distance(a[i-1], a[i]);
    38       else if (k == i-1)
    39       {
    40         d[i][k] = DBL_MAX;
    41         for (u = 0; u < i-1; u++)
    42         {
    43           double tmp = d[i-1][u] + distance(a[u], a[i]);
    44           if (tmp < d[i][k])
    45             d[i][k] = tmp;
    46         }
    47       }
    48       else if (k == i && i == rows - 1)//当k==i时,只需计算都等于rows-1的情况,其他没必要
    49       {
    50         d[i][k] = DBL_MAX;
    51         for (u = 0; u < i-1; u++)
    52         {
    53           double tmp = d[i-1][u] + distance(a[u], a[i]) + distance(a[i-1], a[i]);
    54           if (tmp < d[i][k])
    55             d[i][k] = tmp;
    56         }
    57       }
    58     }
    59   }
    60   return d[rows-1][rows-1];
    61 }
    62 
    63 int main()
    64 {
    65   int rows;
    66 
    67   scanf("%d", &rows);
    68   point* data = (point*)malloc(sizeof(point)*rows);
    69   point p;
    70   int count = 0;
    71   while (rows--)
    72   {
    73     //为了省略排序算法,这里的输入必须按照x坐标从小到达进行
    74     scanf("%d%d", &(p.x), &(p.y));
    75     data[count++] = p;
    76   }
    77   printf("%f\n", process(data, count));
    78   free(data);
    79   
    80   return 0;
    81 }

    CLRS 15-2 整齐打印

    考虑在一个打印机上整齐地打印一段文章的问题。输入的正文是n个长度分别为L1、L2、……、Ln(以字符个数度量)的单词构成的序列。我们希望将这个段落在一些行上整齐地打印出来,每行至多M个字符。“整齐度”的标准如下:如果某一行包含从i到j的单词(i<j),且单词之间只留一个空格,则在行末多余的空格字符个数为 M - (j-i) - (Li+ …… + Lj),它必须是非负值才能让该行容纳这些单词。我们希望所有行(除最后一行)的行末多余空格字符个数的立方和最小。请给出一个动态规划的算法,来在打印机整齐地打印一段又n个单词的文章。分析所给算法的执行时间和空间需求。

    解答:

    定义remain[i, j] = M - j + i - ∑lk ,其中k = i, ..., j,表示余下的空格数

    定义cube[i, j],表示每行空格数的立方值,MAX表示无穷大

                     |------>MAX                 当remain[i, j] < 0时

    cube[i, j] = |------>0                      当j == n,且remain[i, j] >= 0 (其实这里表示的就是最后一行)

                     |------>(remain[i, j])3     非上述两种情况时

    定义所有立方之和sum[i],假设sum[j]表示的是1,...,j这j个单词的最优排列(即所求立方和最小),那么在最后一行,假设是i,...,j这些单词,那么sum[j] = sum[i-1] + cube[i, j]。

                 |------>0                                                 if j == 0

    sum[j] = |

                 |------>min(sum[i - 1] + cube[i - 1, j]       if j > 0,其中1 <= i <= j

     1 GET-REMAIN()
     2 {
     3   for (i = 1; i <= n; i++)
     4     remain[i, i] = M - li;
     5       for (j = i + 1; j <= n; j++)
     6         remain[i, j] = remain[i, j-1] - lj - 1;
     7 }
     8 
     9 GET-CUBE()
    10 {
    11   for (i = 1; i <= n; i++)
    12     for (j = i; j <= n; j++)
    13     {
    14       if (remain[i, j] < 0)
    15         cube[i, j] = MAX;
    16       else if (j==n && remain[i, j]>=0)
    17         cube[i, j] = 0;
    18       else
    19         cube[i, j] = (remain[i, j])3;
    20     }
    21 }
    22 
    23 GET-SUM()
    24 {
    25   sum[0] = 0;
    26   for (j = 1; j <= n; j++)
    27   {
    28     sum[j] = MAX;
    29     for (i = 1; i <= j; i++)
    30       if (sum[i-1] + cube[i, j] < sum[j])
    31       {
    32         sum[j] = sum[i-1] + cube[i, j];
    33         p[j] = i;//用数组p来记录换行的位置
    34       }
    35   }
    36 }
  • 相关阅读:
    ubuntu 安装 库文件
    java接口定义的静态方法和默认如何在类实现的时候使用
    通过java的i/o机制进行图片流的存储以及对网络图片的存储
    java的两种冒泡算法
    Java的Integer和int有什么区别
    使用jq的ajax实现对xml文件的读取
    typescript中的工具 tsd
    使用reactjs遇到Warning: setState(...): Can only update a mounted or mounting component.
    在create-react-app的脚手架里面使用scss
    使用fetch-jsonp进行跨域以及参数的传递
  • 原文地址:https://www.cnblogs.com/null00/p/2497355.html
Copyright © 2020-2023  润新知