Tiết kiệm biến cho các bài toán quy hoạch động
Nguyễn Xuân Huy
Các bài toán quy hoạch động (QHĐ) chiếm một vị trí kháquan trọng trong việc tổ chức
hoạt động và sản xuất. Chính vì lẽ đó mà trongcác kỳ thi học sinh giỏi Quốc gia và Quốc tế
chúng ta thường gặp loại toán này.Thông thường những bạn nào dùng phương pháp quay
lui, vét cạn cho các bài toánQHĐ thì chỉ có thể vét được các tập dữ liệu nhỏ, kích thước
chừng vài chụcbyte. Nếu tìm được đúng hệ thức thể hiện bản chất QHĐ của bài toán và
khéo tổchức dữ liệu thì ta có thể xử lý được những tập dữ liệu khá lớn.
Có thể tóm lược nguyên lý QHĐ do Bellman phát biểu nhưsau: Quy hoạch động là lớp các
bài toán mà quyết định ở bước thứ i phụthuộc vào quyết định ở các bước đã xử lý trước
đó.
Để giải các bài toán QHĐ ta có thể theo sơ đồ sau đây:
Sơ đồ giải bài toán QHĐ:
1.Lập hệ thức: Lập hệ thức biểu diễn tương quan quyết định củabước đang xử lý với các
bước đã xử lý trước đó. Hệ thức này thường là các biểuthức đệ quy do đó dễ gây ra
hiện tượng tràn miền nhớ khi ta tổ chức chươngtrình trực tiếp bằng đệ quy.
2. Tổchức dữ liệu và chương trình: Tổ chức dữ liệu tính toán dần theo từngbước. Nên
tìm cách khử đệ quy. Thông thường, trong các bài toán chúng ta haygặp đòi hỏi một
vài mảng hai chiều.
3. Làmtốt: Làm tốt thuật toán bằng cách thu gọn hệ thức QHĐ và giảmkích thước miền
nhớ.
Dưới đây là thí dụ minh họa.
Bài toán 1: (Chia thưởng) Cầnchía hết m phần thưởng cho n học sinh sắp theo thứ tự từ
giỏi trở xuống sao chomỗi bạn không nhận ít phần thưởng hơn bạn xếp sau mình.
Bài giải:
1. Lập hệ thức: Gọi Chiăm,n) là số cáchchia m phần thưởng cho n học sinh, ta thấy:
1.1. Nếu không có học sinh nào(n=0) thì không có cách chia nào (Chia=0).
1.2. Nếu không có phần thưởngnào (m=0) thì chỉ có một cách chia (Chia =1 - mỗi
học sinh nhận 0 phần thưởng).Ta cũng quy ước Chia (0,0)=1.
1.3. Nếu số phần thưởng íthơn số học sinh (m < n) thì từ học sinh thứ m+1 trở đi sẽ
không được nhận phần thưởng nào, tức là m phần thưởng chỉ có thể chia tối đã cho
các trị của đầu vào khác nhau và điền vào một mảng hai chiều cc. Mảngcc được mô tả như
sau:
const MN=100;{gioi han tren cua m va n}
var cc:array[0..MN,0..MN] of longint;
Ta quy ước cc[i,j] là số cách chia i phần thưởng cho jhọc sinh.
Theo phân tích của phương án 1, ta có:
-cc[0,0] = 1; cc[i,0] = 0, với i:=1..m.
-cc[i,j] = cc[i,i], nếu i < j
-cc[i,j] = cc[i,j-1]+cc[i-j,j], nếu i ≥ j.
Từ đó ta suy ra quy trình điền trị vào bảng cc như sau:
Khởi trị: Khởi trị cột đầu tiên (tức cột 0)toàn 0. Riêng cc[0,0]:=1.
Điền bảng: Lần lượt điền theo từngcột j:=1..n. Tại mỗi cột j ta đặt
-cc[i,j]:=cc[i,i] với i:=0..j-1 < j và
-cc[i,j]:=cc[i,j-1]+cc[i-j,j] với i:=j..m ≥ j.
Nhận kết quả: Sau khi điền bảng, giátrị cc[m,n] chính là kết quả cần tìm.
{PHUONG AN 2: dung mang 2 chieu cc}
function Chia2(m,n: integer):longint;
var i,j: integer;
begin
cc[0,0]:=1;
{Chia 1..vat cho 0 nguoi}
for i:=1 to m do cc[i,0]:=0;
for j:=1 to n do
begin
for i:=0 to j-1 do cc[i,j]:=cc[i,j-1];
for i:=j to m do cc[i,j]:=cc[i,j-1]+cc[i-j,j];
end;
Chia2:=cc[m,n];
end;
Làm tốt lần 2: Dùng mảng 2 chiều chúng tachỉ có thể tính toán được với dữ liệu nhỏ, cỡ
Ngoài ra bạn khai báo hai biến n và m dùng chung cho cácthủ tục:
var m,n: integer;{so phan thuong va so hoc sinh}
Sau đó bạn tạo cho mỗi phương án một test như sau:
procedure Test1;
begin
write('Test1: De quy ');
t:=Nhip;
write(' Ket qua: ',Chiăm,n));