Tải bản đầy đủ - 0 (trang)
§2. PHƯƠNG PHÁP QUY HOẠCH ĐỘNG

§2. PHƯƠNG PHÁP QUY HOẠCH ĐỘNG

Tải bản đầy đủ - 0trang

150



Chun đề



một bài tốn lớn, ta chia nó làm nhiều bài tốn con cùng dạng với nó để có thể giải quyết độc

lập. Trong phương pháp quy hoạch động, nguyên lý này càng được thể hiện rõ: Khi không

biết cần phải giải quyết những bài toán con nào, ta sẽ đi giải quyết tất cả các bài toán con và

lưu trữ những lời giải hay đáp số của chúng với mục đích sử dụng lại theo một sự phối hợp

nào đó để giải quyết những bài tốn tổng qt hơn. Đó chính là điểm khác nhau giữa Quy

hoạch động và phép phân giải đệ quy và cũng là nội dung phương pháp quy hoạch động:

Phép phân giải đệ quy bắt đầu từ bài toán lớn phân rã thành nhiều bài tốn con và đi giải

từng bài tốn con đó. Việc giải từng bài toán con lại đưa về phép phân rã tiếp thành nhiều

bài toán nhỏ hơn và lại đi giải tiếp bài tốn nhỏ hơn đó bất kể nó đã được giải hay chưa.

Quy hoạch động bắt đầu từ việc giải tất cả các bài toán nhỏ nhất ( bài tốn cơ sở) để từ đó

từng bước giải quyết những bài toán lớn hơn, cho tới khi giải được bài tốn lớn nhất (bài

tốn ban đầu).

Ta xét một ví dụ đơn giản:

Dãy Fibonacci là dãy vô hạn các số nguyên dương F[1], F[2], … được định nghĩa như sau:



⎧⎪1, if i ≤ 2

F [i ] = ⎨

⎪⎩F [i − 1] + F [i − 2] , if i ≥ 3

Hãy tính F[6]

Xét hai cách cài đặt chương trình:

Cách 1

program Fibo1;

function F(i: Integer): Integer;

begin

if i < 3 then F := 1

else F := F(i - 1) + F(i - 2);

end;

begin

WriteLn(F(6));

end.



Cách 2

program Fibo2;

var

F: array[1..6] of Integer;

i: Integer;

begin

F[1] := 1; F[2] := 1;

for i := 3 to 6 do

F[i] := F[i - 1] + F[i - 2];

WriteLn(F[6]);

end.



Cách 1 có hàm đệ quy F(i) để tính số Fibonacci thứ i. Chương trình chính gọi F(6), nó sẽ gọi

tiếp F(5) và F(4) để tính … Q trình tính tốn có thể vẽ như cây dưới đây. Ta nhận thấy để

tính F(6) nó phải tính 1 lần F(5), hai lần F(4), ba lần F(3), năm lần F(2), ba lần F(1).



ĐHSPHN 1999-2004



Quy hoạch động



151



F(6)



F(5)



F(4)



F(4)



F(3)



F(2)



F(3)



F(2)



F(2)



F(3)



F(1)



F(2)



F(2)



F(1)



F(1)



Hình 49: Hàm đệ quy tính số Fibonacci



Cách 2 thì khơng như vậy. Trước hết nó tính sẵn F[1] và F[2], từ đó tính tiếp F[3], lại tính tiếp

được F[4], F[5], F[6]. Đảm bảo rằng mỗi giá trị Fibonacci chỉ phải tính 1 lần.

(Cách 2 còn có thể cải tiến thêm nữa, chỉ cần dùng 3 giá trị tính lại lẫn nhau)

Trước khi áp dụng phương pháp quy hoạch động ta phải xét xem phương pháp đó có thoả

mãn những yêu cầu dưới đây hay khơng:

Bài tốn lớn phải phân rã được thành nhiều bài toán con, mà sự phối hợp lời giải của các

bài tốn con đó cho ta lời giải của bài tốn lớn.

Vì quy hoạch động là đi giải tất cả các bài tốn con, nên nếu khơng đủ khơng gian vật lý

lưu trữ lời giải (bộ nhớ, đĩa…) để phối hợp chúng thì phương pháp quy hoạch động cũng

khơng thể thực hiện được.

Q trình từ bài tốn cơ sở tìm ra lời giải bài tốn ban đầu phải qua hữu hạn bước.

Các khái niệm:

Bài toán giải theo phương pháp quy hoạch động gọi là bài tốn quy hoạch động

Cơng thức phối hợp nghiệm của các bài toán con để có nghiệm của bài tốn lớn gọi là

cơng thức truy hồi (hay phương trình truy tốn) của quy hoạch động

Tập các bài tốn nhỏ nhất có ngay lời giải để từ đó giải quyết các bài tốn lớn hơn gọi là cơ

sở quy hoạch động

Không gian lưu trữ lời giải các bài tốn con để tìm cách phối hợp chúng gọi là bảng

phương án của quy hoạch động

Các bước cài đặt một chương trình sử dụng quy hoạch động:

Giải tất cả các bài tốn cơ sở (thơng thường rất dễ), lưu các lời giải vào bảng phương án.

Dùng công thức truy hồi phối hợp những lời giải của những bài tốn nhỏ đã lưu trong bảng

phương án để tìm lời giải của những bài toán lớn hơn và lưu chúng vào bảng phương án.

Cho tới khi bài toán ban đầu tìm được lời giải.

Dựa vào bảng phương án, truy vết tìm ra nghiệm tối ưu.



Lê Minh Hồng



152



Chun đề



Cho đến nay, vẫn chưa có một định lý nào cho biết một cách chính xác những bài tốn nào có

thể giải quyết hiệu quả bằng quy hoạch động. Tuy nhiên để biết được bài tốn có thể giải bằng

quy hoạch động hay khơng, ta có thể tự đặt câu hỏi: “Một nghiệm tối ưu của bài tốn lớn có

phải là sự phối hợp các nghiệm tối ưu của các bài toán con hay khơng ?” và “Liệu có thể

nào lưu trữ được nghiệm các bài tốn con dưới một hình thức nào đó để phối hợp tìm

được nghiệm bài tốn lớn"



ĐHSPHN 1999-2004



Quy hoạch động



153



§3. MỘT SỐ BÀI TỐN QUY HOẠCH ĐỘNG

3.1. DÃY CON ĐƠN ĐIỆU TĂNG DÀI NHẤT

Cho dãy số nguyên A = a[1..n]. (n ≤ 106, -106 ≤ a[i] ≤ 106). Một dãy con của A là một cách

chọn ra trong A một số phần tử giữ nguyên thứ tự. Như vậy A có 2n dãy con.

u cầu: Tìm dãy con đơn điệu tăng của A có độ dài lớn nhất.

Ví dụ: A = (1, 2, 3, 4, 9, 10, 5, 6, 7). Dãy con đơn điệu tăng dài nhất là: (1, 2, 3, 4, 5, 6, 7).

Input: file văn bản INCSEQ.INP

Dòng 1: Chứa số n

Dòng 2: Chứa n số a[1], a[2], …, a[n] cách nhau ít nhất một dấu cách

Output: file văn bản INCSEQ.OUT

Dòng 1: Ghi độ dài dãy con tìm được

Các dòng tiếp: ghi dãy con tìm được và chỉ số những phần tử được chọn vào dãy con đó.

INCSEQ.INP

11

1 2 3 8 9 4 5 6 20 9 10



INCSEQ.OUT

8

a[1] = 1

a[2] = 2

a[3] = 3

a[6] = 4

a[7] = 5

a[8] = 6

a[10] = 9

a[11] = 10



Cách giải:

Bổ sung vào A hai phần tử: a[0] = -∞ và a[n+1] = +∞. Khi đó dãy con đơn điệu tăng dài nhất

chắc chắn sẽ bắt đầu từ a[0] và kết thúc ở a[n+1].

Với ∀ i: 0 ≤ i ≤ n + 1. Ta sẽ tính L[i] = độ dài dãy con đơn điệu tăng dài nhất bắt đầu tại a[i].



3.1.1. Cơ sở quy hoạch động (bài toán nhỏ nhất):

L[n+1] = Độ dài dãy con đơn điệu tăng dài nhất bắt đầu tại a[n+1] = +∞. Dãy con này chỉ

gồm mỗi một phần tử (+∞) nên L[n+1] = 1.



3.1.2. Công thức truy hồi:

Giả sử với i chạy từ n về 0, ta cần tính L[i]: độ dài dãy con tăng dài nhất bắt đầu tại a[i]. L[i]

được tính trong điều kiện L[i+1..n+1] đã biết:

Dãy con đơn điệu tăng dài nhất bắt đầu từ a[i] sẽ được thành lập bằng cách lấy a[i] ghép vào

đầu một trong số những dãy con đơn điệu tăng dài nhất bắt đầu tại vị trí a[j] đứng sau a[i]. Ta

sẽ chọn dãy nào để ghép a[i] vào đầu? Tất nhiên là chỉ được ghép a[i] vào đầu những dãy con

bắt đầu tại a[j] nào đó lớn hơn a[i] (để đảm bảo tính tăng) và dĩ nhiên ta sẽ chọn dãy dài nhất

để ghép a[i] vào đầu (để đảm bảo tính dài nhất). Vậy L[i] được tính như sau: Xét tất cả các



Lê Minh Hoàng



154



Chuyên đề



chỉ số j trong khoảng từ i + 1 đến n + 1 mà a[j] > a[i], chọn ra chỉ số jmax có L[jmax] lớn

nhất. Đặt L[i] := L[jmax] + 1:

L [i ] = max L [ j] + 1

i < j≤ n −1

a [i ]< a [ j]



3.1.3. Truy vết

Tại bước xây dựng dãy L, mỗi khi gán L[i] := L[jmax] + 1, ta đặt T[i] = jmax. Để lưu lại rằng:

Dãy con dài nhất bắt đầu tại a[i] sẽ có phần tử thứ hai kế tiếp là a[jmax].

Sau khi tính xong hay dãy L và T, ta bắt đầu từ T[0].

T[0] chính là phần tử đầu tiên được chọn,

T[T[0]] là phần tử thứ hai được chọn,

T[T[T[0]]] là phần tử thứ ba được chọn …

Q trình truy vết có thể diễn tả như sau:

i := T[0];

while i <> n + 1 do {Chừng nào chưa duyệt đến số a[n+1]=+∞ ở cuối}

begin



i := T[i];

end;



Ví dụ: với A = (5, 2, 3, 4, 9, 10, 5, 6, 7, 8). Hai dãy L và T sau khi tính sẽ là:

Calculating



i

0

ai − ∞

L[i] 9



1

5

5



2

2

8



3

3

7



4

4

6



5

9

3



6

10

2



7

5

5



8

6

4



9

7

3



10

8

2



T[i]



8



3



4



7



6



11



8



9



10



11



2



11

+∞

1



Tracing



Hình 50: Tính tốn và truy vết

P_3_03_1.PAS * Tìm dãy con đơn điệu tăng dài nhất

{$MODE DELPHI} (*This program uses 32-bit Integer [-231..231 - 1]*)

program LongestSubSequence;

const

InputFile = 'INCSEQ.INP';

OutputFile = 'INCSEQ.OUT';

max = 1000000;

var

a, L, T: array[0..max + 1] of Integer;

n: Integer;

procedure Enter;

var

i: Integer;

f: Text;

begin

Assign(f, InputFile); Reset(f);

ReadLn(f, n);

ĐHSPHN 1999-2004



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

§2. PHƯƠNG PHÁP QUY HOẠCH ĐỘNG

Tải bản đầy đủ ngay(0 tr)

×