算法导论15-1双调欧几里得旅行商问题 & 15-2整齐打印

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 }

posted on 2012-05-13 12:47  NULL00  阅读(4869)  评论(1编辑  收藏  举报

导航