PHƯƠNG PHÁP QUY HOẠCH ĐỘNG Một số bài toán Quy hoạch động Phương pháp quy hoạch động dùng để giải bài toán tối ưu có bản chất đệ quy, tức là việc tìm phương án tối ưu cho bài toán đó có thể đưa về tìm phương án tối ưu của một số hữu hạn các bài toán con Đối với nhiều thuật toán đệ quy chúng ta đã tìm hiểu, nguyên lý chia để trị (divide and conquer) thường đóng vai trò chủ đạo trong việc thiết kế thuật toán Để giải quyết một bài toán lớn, ta chia nó làm nhiều bài toán con cùng dạng với nó để có t.
Một số toán Quy hoạch động Phương pháp quy hoạch động dùng để giải tốn tối ưu có chất đệ quy, tức việc tìm phương án tối ưu cho tốn đưa tìm phương án tối ưu số hữu hạn toán Đối với nhiều thuật toán đệ quy tìm hiểu, nguyên lý chia để trị (divide and conquer) thường đóng vai trị chủ đạo việc thiết kế thuật toán Để giải tốn lớn, ta chia làm nhiều tốn dạng với để giải độc lập Trong phương pháp quy hoạch động, nguyên lý thể rõ: Khi cần phải giải toán nào, ta giải tất toán lưu trữ lời giải hay đáp số chúng với mục đích sử dụng lại theo phối hợp để giải tốn tổng qt Đó điểm khác Quy hoạch động phép phân giải đệ quy nội dung phương pháp quy hoạch động: Phép phân giải đệ quy toán lớn phân rã thành nhiều toán giải tốn Việc giải tốn lại đưa phép phân rã tiếp thành nhiều toán nhỏ lại giải tiếp toán nhỏ giải hay chưa Quy hoạch động việc giải tất toán nhỏ ( toán sở) để từ bước giải toán lớn hơn, giải toán lớn (bài tốn ban đầu) Ta xét ví dụ đơn giản: Dãy Fibonacci dãy vô hạn số nguyên dương F[1], F[2], … định nghĩa sau: F[i]=1, if i ≤ F[ i ] = { F[ i −1] + F[ i − 2], if i ≥ Hãy tính F[6] Xét hai cách cài đặt chương trình: Cách Cách program Fibo1; program Fibo2; var F: array[1 6] of Integer; i: Integer; function F(i: Integer): Integer; begin if i < then F := else F := F(i - 1) + F(i - 2); end; begin F[1] := 1; F[2] := 1; for i := to F[i] := F[i - 1] + F[i - 2]; WriteLn(F[6]); end begin WriteLn(F(6)); end Cách có hàm đệ quy F(i) để tính số Fibonacci thứ i Chương trình gọi F(6), gọi tiếp F(5) F(4) để tính … Quá trình tính tốn vẽ Ta nhận thấy để tính F(6) phải tính 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) -1- Một số toán Quy hoạch động Cá ch khơng Trước hết tính sẵn F[1] F[2], từ tính tiếp F[3], lại tính tiếp F[4], F[5], F[6] Đảm bảo giá trị Fibonacci phải tính lần (Cách cịn cải tiến thêm nữa, cần dùng giá trị tính lại lẫn nhau) Trước á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 u cầu hay khơng: Bài tốn lớn phải phân rã thành nhiều toán con, mà phối hợp lời giải tốn cho ta lời giải tốn lớn Vì quy hoạch động giải tất toán con, nên không đủ không gian vật lý lưu trữ lời giải (bộ nhớ, đĩa…) để phối hợp chúng phương pháp quy hoạch động khơng thể thực Q trình từ tốn sở tìm lời giải toá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 toán quy hoạch động - Công thức phối hợp nghiệm tốn để có nghiệm tốn lớn gọi cơng thức truy hồi (hay phương trình truy toán) quy hoạch động - Tập toán nhỏ có lời giải để từ giải toán lớn gọi sở quy hoạch động - Không gian lưu trữ lời giải tốn để tìm cách phối hợp chúng gọi bảng phương án quy hoạch động Các bước cài đặt chương trình sử dụng quy hoạch động: - Giải tất toán sở (thông thường dễ), lưu lời giải vào bảng phương án Dùng công thức truy hồi phối hợp lời giải toán nhỏ lưu bảng phương án để tìm lời giải toán lớn lưu chúng vào phương án Cho tới tốn ban đầu tìm lời giải - Dựa vào bảng phương án, truy vết tìm nghiệm tối ưu Cho đến nay, chưa có định lý cho biết cách xác tốn giải hiệu quy hoạch động Tuy nhiên để biết tốn giải quy hoạch động hay khơng, ta tự đặt câu hỏi: “Một nghiệm tối ưu tốn lớn có phải phối hợp nghiệm tối ưu toán hay khơng ?” “Liệu lưu trữ nghiệm tốn hình thức để phối hợp tìm nghiệm tốn lớn" MỘT SỐ BÀI TỐN QHĐ CƠ BẢN Bài tốn 1: Dãy đơn điệu tăng dài nhất: -2- Một số toán Quy hoạch động Cho dãy A gồm n số nguyên, ký hiệu [a0, a1, , an-1] Tìm dãy đơn điệu tăng dài dãy A, biết dãy A dãy có từ A cách xóa số phần tử A Ví dụ: dãy [1, 5, 9, 2, 3, 11, 8, 10, 4] có dãy đơn điệu tăng dài [1, 2, 3, 8, 10] Input: đọc từ tệp Day.inp gồm số nguyên dãy A viết cách dấu cách Output: Ghi kết tính vào tệp Day.out gồm dòng - Dòng 1: ghi độ dài dãy dài - Dòng 2: Ghi phần tử dãy Cách giải: Bổ sung vào a phần tử a[0]=-∞ a[n+1]=+∞ dãy đơn điệu dài chắn a[0] kết thúc a[n+1] với ∀ i ≤ i ≤ n+1 ta tính L[i] độ dại dãy đơn điệu tăng dài bắt đầu a[i] * sở QHĐ L[n+1]=1 dãy gồm phần tử +∞ * Công thức truy hồi Giả sử với i chạy từ n 0, ta cần tính L[i]: độ dài dãy tăng dài bắt đầu a[i] L[i] tính điều kiện L[i+1 n+1] biết: Dãy đơn điệu tăng dài a[i] thành lập cách lấy a[i] ghép vào đầu số dãy đơn điệu tăng dài bắt đầu vị trí a[j] đứng sau a[i] Ta chọn dãy để ghép a[i] vào đầu? Tất nhiên ghép a[i] vào đầu dãy bắt đầu a[j] lớn a[i] (để đảm bảo tính tăng) dĩ nhiên ta chọn dãy dài để ghép a[i] vào đầu (để đảm bảo tính dài nhất) Vậy L[i] tính sau: Xét tất số j khoảng từ i + đến n + mà a[j] > a[i], chọn số jmax có L[jmax] lớn Đặt L[i] := L[jmax] + 1: L [i ] = max L [ j] +1 i < j≤ n −1 a [ i ]< a [ j] * Truy vết Tại bước xây dựng dãy L, gán L[i] := L[jmax] + 1, ta đặt T[i] = jmax Để lưu lại rằng: Dãy dài bắt đầu a[i] có phần tử thứ hai a[jmax] Sau tính xong hay dãy L T, ta T[0] T[0] phần tử chọn, T[T[0]] phần tử thứ hai chọn, T[T[T[0]]] phần tử thứ ba chọn … Quá trình truy vết diễn tả sau: i := T[0]; while i n + {Chừng chưa duyệt đến số a[n+1]=+∞ cuối} begin -3- Một số toán Quy hoạch động i := T[i]; end; Ví dụ: với A = (5, 2, 3, 4, 9, 10, 5, 6, 7, 8) Hai dãy L T sau tính là: i 10 11 − ∞ L[i ] 9 10 +∞ T[i]2 11 10 11 Cách 1: Duyệt từ cuối dãy đầu dãy Const Max = 5000; Var a,L,T:array [0 Max] of integer; i,j,n,jmax:integer; fi,fo:text; Procedure Nhapdulieu; begin assign(fi,'day.inp'); reset(fi); N:=0; while not eof (fi) begin N:=N+1; read(fi,a[N]); end; close(fi); end; BEGIN assign(fo,'day.out'); rewrite(fo); Nhapdulieu; a[0]:= low(integer);a[n+1]:=high(integer); {thêm phần tử canh đầu dãy} L[n+1]:= 1; {Khởi tạo sở QHĐ} for i:= n downto {tính bảng phương án} begin {chọn số j đứng sau i thỏa mãn a[j]>a[i] chọn jmax có L[jmax] lớn nhất} jmax:=n+1; for j:=i+1 to n+1 if (a[i]L[jmax]) then jmax:=j; L[i] := L[jmax]+1; {lưu độ dài dãy tăng dài ai} T[i] := jmax; {lưu vết: phần tử đứng liền sau a[i] dãy tăng dài a[jmax]} end; Writeln(fo,L[0]-2); { độ dài dãy tăng dài =L[0]-2 } i:=T[0]; {bắt đầu truy vết tìm nghiệm} while in+1 begin -4- Một số toán Quy hoạch động write(fo,a[i],' '); i:=T[i]; end; close(fo); END Cách 2: Duyệt từ đầu dãy cuối dãy Const Max = Var a,L,T:array [0 Max] i,j,n,jmax:integer; Procedure begin assign(fi,'day.inp'); reset(fi); while not eof 5000; integer; fi,fo:text; Nhapdulieu; of N:=0; begin read(fi,a[N]); end; (fi) N:=N+1; close(fi); end; Procedure Begin quyhoachdong; for if End; Procedure Begin L[0]:=1;a[0]:=low(integer);a[N+1]:=high(integer); i:=1 to N+1 begin jmax:=0; for j:=0 to i-1 (a[j]L[jmax]) then jmax:=j; L[i]:=L[jmax]+1; T[i]:=jmax; end; Truyvet(i:integer); {dùng đệ quy if để in i=0 dãy then write(fo,a[i],' end; Procedure assign(fo,'day.out'); writeln(fo,'so luong phan tối ưu} i:=T[i]; exit; truyvet(i); '); ghidulieu; Begin rewrite(fo); quyhoachdong; tu=',l[n+1]-2); truyvet(N+1); close(fo); End; BEGIN Nhapdulieu; Ghidulieu; -5- Một số toán Quy hoạch động END Bài toán 2: Mê cung Trong chuyến thám hiểm mạo hiểm, đồn thám hiểm khơng may lọt vào mê cung với nhiều cạm bẫy Trong mê cung có lối nhất, lối bao gồm hình vng xếp thành hàng dài Muốn người phải bước qua hàng hình vng phải bước theo quy tắc sau: - Quy tắc 1: Mỗi bước bước ơ, hai ô ba ô - Quy tắc 2: Từ người thứ trở bước theo quy tắc không trùng với cách bước tất người trước Hỏi đồn thám hiểm cịn lại tối thiểu người khơng thể khỏi mê cung Dữ liệu vào: mecung.inp - Dòng 1: ghi số nguyên m (m