算法導論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 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM