動態規劃程序設計(5)

【例】求最長不降低序列

【問題描述】:算法

設有由n個不相同的整數組成的數列,記爲:b(1)、b(2)、……、b(n)且b(i)<>b(j)  (i<>j),若存在i1<i2<i3< … < ie 且有b(i1)<b(i2)< … <b(ie)則稱爲長度爲e的不降低序列。程序要求,當原數列給出以後,求出最長的不降低序列。數組

例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。例中13,16,18,19,21,22,63就是一個長度爲7的不降低序列,同時也有7 ,9,16,18,19,21,22,63長度爲8的不降低序列。數據結構

【算法分析】:根據動態規劃的原理,由後往前進行搜索。ide

1)、對b(n)來講,因爲它是最後一個數,因此當從b(n)開始查找時,只存在長度爲1的不降低序列;spa

2)、若從b(n-1)開始查找,則存在下面的兩種可能性:code

①若b(n-1)<b(n)則存在長度爲2的不降低序列b(n-1),b(n)。blog

②若b(n-1)>b(n)則存在長度爲1的不降低序列b(n-1)或b(n)。ci

3)、通常若從b(i)開始,此時最長不降低序列應該按下列方法求出:it

在b(i+1),b(i+2),…,b(n)中,找出一個比b(i)大的且最長的不降低序列,做爲它的後繼。event

【數據結構】:

爲算法上的須要,定義一個數組整數類型二維數組b(N,3)

1)、b(i,1)表示第i個數的數值自己;

2)、b(i,2)表示從i位置到達N的最長不降低序列長度

3)、b(i,3)表示從i位置開始最長不降低序列的下一個位置,若b[i,3]=0則表示後面沒有鏈接項。

【求解過程】:

①    從倒數第二項開始計算,後面僅有1項,比較一次,因63>15,不符合要求,長度仍爲1

②    從倒數第三項開始其後有2項,需作兩次比較,獲得目前最長的不降低序列爲2,以下表:

 

11

12

13

14

……

 

11

12

13

14

 

 

22

63

15

……

 

21

22

63

15

 

 

2

1

1

……

 

3

2

1

1

 

 

13

0

0

……

 

12

13

0

0

【通常處理過程】:

①在i+1,i+2,…,n項中,找出比b[I,1]大的最長長度L以及位置K;

②若L>0,則b[I,2]:=L+1;b[I,3]:=k;

最後本題通過計算,其數據存儲表以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

13

7

9

16

38

24

37

18

44

19

21

22

63

15

7

8

7

6

3

4

3

5

2

4

3

2

1

1

4

3

4

8

9

7

9

10

13

11

12

13

0

0

 

 1 初始化:  2 for i:=1 to n do
 3 begin
 4   read(b[i,1]);  5   b[i,2]:=1;b[i,3]:=0;  6 end;  7 下面給出求最長不降低序列的算法:  8 for i:=n-1 downto 1 do
 9 begin
10  L:=0;k:=0; 11  for j:=i+1 to n do
12   if(b[j,1]>b[i,1])and(b[j,2]>L) then begin
13 L:=b[j,2]; 14 k:=j; 15                                       end; 16  if L>0 then begin
17 b[i,2]:=L+1; 18 b[i,3]:=k; 19              end; 20 end; 21 下面找出最長不降低序列: 22 k:=1; 23 for j:=1 to n do
24  if b[j,2]>b[k,2] then k:=j; 25 最長不降低序列長度爲B(k, 2)序列 26 while k<>0  do
27 begin
28  write(b[k,1]:4); 29  k:=b[k,3]; 30 end;
 1 var 
 2 n,i,L,k,j:integer;
 3   b:array[1..100,1..3]of integer;
 4 begin
 5  readln(n);
 6  for i:=1 to n do
 7  begin
 8   read(b[i,1]);
 9   b[i,2]:=1;b[i,3]:=0;
10  end;
11 for i:=n-1 downto 1 do
12   begin
13    L:=0;k:=0;
14    for j:=i+1 to n do
15     if(b[j,1]>b[i,1])and(b[j,2]>L) then begin
16 L:=b[j,2]; k:=j;
17                                          end;
18   if L>0 then begin
19                b[i,2]:=L+1;b[i,3]:=k;
20               end;
21  end;
22  k:=1;
23  for j:=1 to n do
24   if b[j,2]>b[k,2] then k:=j;
25  writeln('max=',b[k,2]);
26  while k<>0  do
27   begin
28     write(b[k,1]:4);
29     k:=b[k,3];
30   end;
31  writeln;
32 end.
參考程序

程序運行結果:

輸入:14

13 7 9 16 38 24 37 18 44 19 21 22 63 15

輸出:max=8

7  9  16  18  19  21  22  63

 1 var
 2   i,n,max,st,en:longint;
 3   b:array[1..100000,1..3] of longint;
 4 procedure Init;
 5 var i:longint;
 6 begin
 7   readln(n);
 8   for i:=1 to n do
 9    begin
10      read(b[i,1]);
11      b[i,2]:=1; b[i,3]:=0;
12    end;
13 end;
14 procedure Lis;
15 var i,j,maxl,loca:longint;
16 begin
17   for i:=n-1 downto 1 do
18    begin
19      maxl:=0; loca:=0;
20      for j:=i+1 to n do
21       if b[i,1]<b[j,1] then
22         //begin
23           if b[j,2]>maxl then
24            begin
25              maxl:=b[j,2];
26              loca:=j;
27            end;
28         //end;
29      if maxl>0 then
30       begin
31         b[i,2]:=maxl+1;
32         b[i,3]:=loca;
33       end;
34    end;
35 end;
36 procedure Findmax;
37 var i:longint;
38 begin
39   max:=0;
40   for i:=1 to n do
41    if b[i,2]>max then
42     begin
43       max:=b[i,2];
44       st:=i;
45     end;
46 end;
47 procedure Outit;
48 var i:longint;
49 begin
50   writeln(max);
51   i:=st;
52   while i<>0 do
53    begin
54      write(b[i,1],' ');
55      i:=b[i,3];
56    end;
57 end;
58 begin
59   Init;
60   Lis;
61   Findmax;
62   Outit;
63 end.
個人板子 O(n²)

 

【例】攔截導彈1

某國爲了防護敵國的導彈襲擊,發展出一種導彈攔截系統。可是這種攔截系統有一個缺陷:雖然它的第一發炮彈可以到達任意的高度,可是之後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲,因爲該系統還在試用階段。因此只有一套系統,所以有可能不能攔截全部的導彈。輸入導彈依次飛來的高度(雷達給出的高度不大於30000的正整數)。計算這套系統最多能攔截多少導彈。

輸入:N顆依次飛來的導彈高度,(導彈個數<=1000)。

輸出:一套系統最多攔截的導彈數,並依次打印輸出被攔截導彈的高度。

在本題中不只要求輸出最優解,並且還要求輸出最優解的造成過程。爲此,咱們設置了一張記憶表C[i],在按從後往前方式求解的過程當中,將每個子問題的最佳決策保存起來,避免在輸出方案時重複計算。

階段i:     由右而左計算導彈n‥導彈1中可攔截的最多導彈數(1≤i≤n);

狀態B[i]:  因爲每一個階段中僅一個狀態,所以可經過一重循環

for i := n-1 downto 1 do  枚舉每一個階段的狀態B[i];

決策k:在攔截導彈i以後應攔截哪一枚導彈可以使得B[i]最大(i+1≤k≤n),

1   2   3   4   5   6   7   8   9   10  11  12  13  14  I

13  7   9   16  38  24  37  18  44  19  21  22  63  15  A[I]    {高度}

2   1   1   2   4   3   3   2   3   2   2   2   2   1   B[I]    {可攔截數}

2   0   0   14  6   8   8   14  10  14  14  14  14  0   C[I]    {再攔截}

 1 var
 2   a,b,c:array[1..1000] of longint;
 3   n,i,j,k,max:longint;
 4 begin
 5  n:=0;                                                           {初始化,讀入數據}
 6  while not eoln do 
 7 begin                                                               {eoln :行結束}
 8 inc(n);read(a[n]); 
 9 b[n]:=1; c[n]:=0;
10   end;
11  readln;
12  for i:=n-1 downto 1 do                         {枚舉每個階段的狀態,設導彈i被攔截}
13 begin                  
14    max:=0; j:=0;
15    for k:=i+1 to n do                      {枚舉決策,計算最佳方案中攔截的下一枚導彈}
16     if (a[k]<=a[i]) and (b[k]>max) then begin  max:=b[k];j:=k; end;
17     b[i]:=max+1; c[i]:=j;                 {若導彈i以後攔截導彈j爲最佳方案,則記下}
18   end;
19   max := 0;
20   for i := 1 to n do                                 {枚舉求出一套系統能攔截的最多導數}
21     if b[i]>max then begin max:=b[i]; j:=i; end;
22   writeln('Max = ',b[j]);                                               {打印輸出結果}
23   while j>0 do 
24 begin
25    write(a[j]:5); j:=c[j];
26     end;
27  end.
【參考程序】(逆推法)
相關文章
相關標籤/搜索