Ví dụ 1. Cho một dãy các số nguyên a,, Qj, a„ và một số nguyên M. Hãy tìm tất cả các dãy con có tổng các phần tử bằng M.
Thuật toán
Cần chia dãy trên thành hai nhóm, sau đó kiểm tra xem tổng các phần tử của nhóm 1 bằng M không. Dùng một biến Tong để tính tổng các phần tử thuộc nhóm 1. Biến Tong phải trả lại giá trị trước đó khi đã kiểm tra một phương án.
Cách 1. Không dùng nhánh cận P r o c e d u r e X e p _ n h o m ( i : I n t e g e r ) ;
V a r j ; I n t e g e r ; B e g i n
F o r j : = 1 t o 2 d o B e g i n
N h o m [ i ] : = j ;
I f j = l t h e n T o n g : = T o n g + a [ i ] ; I f ( i = n ) t h e n
b e g i n
i f (Tong=M) t h e n K e t Q u a ; e n d
E l s e X e p _ n h o m ( i + l ) ;
I f j = l t h e n T o n g : = T o n g - a [ i ] ; E n d ;
E n d ;
trong đó, thủ tục KetQua hiển thị kết quả của bài toán.
Cách 2. Dùng nhánh cận
Trong quá trình phân nhóm và tính tổng, nếu ở bước nào đó mà giá trị biến Tong lớn hơn M thì không phát triển theo phương án này. Thuật toán nhánh cận có dạng:
P r o c e d u r e X e p _ n h o m ( i : I n t e g e r ) ; V a r j : I n t e g e r ;
B e g i n
F o r j : = 1 t o 2 d o B e g i n
N h o m [ i ] : = j ;
I f j = l t h e n T o n g : = T o n g + a [ i ] ; I f i = n t h e n K e t Q u a
E l s e I f T o n g < M t h e n X e p _ n h o m ( i + 1 ) ; I f j = l t h e n T o n g : = T o n g - a [ i ] ;
E n d ; E n d ;
162 21.CTDL. .PTPM ẾM .B
Như vậy, thực ra nhánh cận là một điều kiện trước khi gọi đệ quy để ngăn chương trình gọi những nhánh chắc chắn không thể cho nghiệm.
Mộl dạng khác của nhánh cận là điều kiện được đưa vào ở cận trên (hoặc cận dưới) của ngay biến lặp trong vòng đệ quy.
Ví dụ 2. Có một máy đổi tiền có thể nhận biết và đổi được các đồng tiền thành tổng các đồng tiền nhỏ hơn trona số các đồng tiền kị, K^, {Kị < Kj< ... < Ẫ'„). Cho một đồng tiền có giá trị M liệt kê tất cả các cách đổi đồng tiền này biết rằng số lượng mỗi đồng tiển loại K, là không hạn chế.
Thuật toán
Dùng đệ quy để tìm số lưựng j đồng tiền loại i để đổi. Khi đó giá trị của / sẽ giới hạn trong khoảng từ 0 đến (M - Tong) div K[i] (với Tong là biến chung dùng để tính tổng số tiền đã đổi được đến bước / -1 ) đây là nhánh cận của bài toán thay vì cho j chọn các giá trị từ j đến co.
Thủ tục đổi tiền có dạng:
P r o c e d u r e D o i _ t i e n ( i ; I n t e g e r ) ; V a r j , k ; I n t e g e r ;
B e g i n
K : = (M - T o n g ) d i v K [ i ] ; { n h á n h cận>
F o r j : = 0 t o k d o B e g i n
A [ i ] : = j ;
T o n g : = T o n g + j * K [ i ] ;
I f ( i = n ) a n d ( T o n g = M) t h e n K e t Q u a E l s e D o i _ t i e n ( i + 1 ) ;
T o p g : = T o n g - j * K [ i ] ; E n d ;
End ;
Thông thường phương pháp nhánh cận dùng để giải những bài toán tối ưu. Để giải những bài toán tối ưu thường duyệt qua từng phương án, với mỗi phương án duyệt qua tính giá của phương án và so sánh với phương án tốt nhất hiện tại đang lưu để quyết định có chọn phương án này như một phương án tối ưu tạm thời không.
Để giải những bài toán này IhưcíỊiíĩ dùnơ nhánh cận. Trong từng bước xây dựng phường án ta đánh giá để biếl được theo hướng xâv dựng phưcíng án này có tốl hơn phương án tối ưu không. Nếu giá của phương án ở bước xây dựng thứ / mà lớn hơn giá tối iru hiện tại thì không phát triển tiếp theo hướng này mà quay lui trở lại để xét phương án khác. Tuy nhiên, có những bài toán không thể giải theo cách này vì giá của một phương án không thể tính khi chưa đủ các thành phần của phương án.
Ví dụ 3. {Bải toán n chànẹ trai và m cô qái) Cho n chàng trai và m cô gái, mỗi chàng trai có mức độ tình cảm khác nhau cho mỗi cô gái. Hãy đưa ra cách sắp các đôi sao cho tổng mức độ tình cảm của các cặp là cao nhất có thể được.
Thủ tục sắp cặp thứ ivới tổng các mức độ tình cảm của / -1 cặp đã sắp là s. Trong thử tục sử dụng biến Max để ghi nhận giá trị tổng mức độ tình cảm lớn nhất tạm thời của các phương án đã xét và biến Cmax lưu mức độ tình cảm cao nhất trong các cặp (dùng cho điều kiện nhánh cận).
P r o c e d u r e S a p C a p ( I :B y t e ; s : I n t e g e r ) ; V a r J ; B y t e ;
B e g i n
I f ( I = N + 1 ) And (S>Max) T h e n B e g i n M a x ; = s ; D : = c ; E n d ; F o r J : = 1 To M Do
I f ( B [ J ] ) And ( A [ I , J ] > 0 ) A nd ( S + C M a x * ( N - I + 1 ) >Max) T h e n B e g i n
C [ I ] : = J ; S : = S + A [ I , J ] ; S a p C a p ( I + l , S ) ; B [ J ] : = T r u e ; S : = S - A [ I , J ] ; e n d ;
C [ I ] : = 0 ; E n d ;
5. QƯY HOẠCH ĐỘ NG (DYNAM IC PRO G RAM M ING )
Tư tưởng quy hoạch động được Bellman đề cập từ 1960 nhằm tìm nghiệm của bài toán tối ưu. Những năm 1960-1970 là thời kì phát triển rực rỡ nhất của phương pháp này, nhờ nó nhiều bài toán với kích thước dữ liệu lổfn hon đã được giải với tốc độ chạy chương trình nhanh hơn so với những cách giải trước đây. Song đến năm 1970 đã xuất hiện một số bài toán mới mà cách giải bằng quy hoạch động tỏ ra hạn chế, chỉ tương đối có hiệu quả hoặc thậm chí có bài toán hầu như không có hiệu quả. Tuy nhiên với hàng loạt bài toán thông dụng, phương pháp này vẫn là một trong các phương pháp tốt.
5.1. PHƯƠNG PHÁP
Trong phương pháp chia để trị, việc giải một bài toán được đưa về giải một số bài toán con (miền dữ liệu nhỏ hơn) rồi tổ hợp nghiêm của các bài toán con để nhận được nghiệm của bài toán cần giải. Để giải được các bài toán con này, ta lại đưa về giải các bài
164
toán con nhỏ hơn (theo cách tiếp cận trên xuống (top-down)). Quá trình trên sẽ tiếp tục cho tới khi nhận được các bài toán con có thể giải được dễ dàng. Phương pháp chia để trị giải các bài toán con nhận được trong quá trình chia nhỏ một cách độc lập với nhau và kết quả của bài toán con này không ảnh hưởng đến bài toán con khác.
Phương pháp quy hoạch động cũng có những điểm giống kĩ thuật chia để trị nhưng trong quá trình phân chia bài toán thành các bài toán con, các kết quả của mỗi bài toán con được lưu lại để xây dựng kết quả bài toán ban đầu và tránh giải nhiều lần cùng một bài toán con. Do đó, phương pháp quy hoạch động thường giải bằng kĩ thuật dưới lên (bottom-up).
5.2. CÁC BƯỚC GIẢI BÀI TOÁN BẰNG PHƯƠNG PHẦP QUY HOẠCH ĐỘNG
Quy hoạch động là một phương pháp dựa vào kĩ thuật dưới-lên. Để giải bài toán, chúng ta thường xuất phát từ những trường hợp riêng, đơn giản nhất của bài toán (thưòfng là thấy ngay nghiệm của chúng). Nghiệm của bài toán trong những trường hợp cụ thể được lưu lại rồi kết hợp theo từng bước để nhận được nghiệm của bài toán có cỡ "lớn hon" và cuối cùng là nhận được nghiêm của bài toán đã cho. Do đó, khi giải một bài toán con ta chỉ cần tìm những nghiêm của các bài toán con có cỡ nhỏ hơn mà kết quả đã được giải và lưu trong bảng kết quả mà không cần giải lại. Chính điều này làm cho các thuật toán quy hoạch động có hiệu quả cao.
Để giải một bài toán bằng phưoíng pháp quy hoạch động, chúng ta phải tiến hành các bước cơ bản sau:
• Tìm nghiệm các bài toán con (các trưòng hợp riêng) của bài toán.
• Tim công thức (hoặc quy tắc) xây dựng nghiêm của các bài toán con thông qua nghiệm của các bài toán con cỡ nhỏ hofn (gọi là công thức truy hồi).
• Tổ chức cấu tnlc dữ liệu hợp lí (thường là dạng bảng) để lưu giữ nghiệm của các bài toán con. Sau đó tính nghiệm của các bài toán con theo công thức đã xây dựng ở bước trước và tiếp tục lưu vào bảng.
• Từ bảng các nghiệm của các bài toán con đã được làm đầy, tìm cách xây dựng lời giải cho bài toán ban đầu.
Các bước trên được liệt kê theo thứ tự nhưng để hình thành các bước này cho một bài toán cụ thể ta phải tổng họfp tất cả các bước, trong đó nhiệm vụ xây dựng công thức truy hổi xác định nghiệm (hoặc một thành phần của nghiệm) mới hình thành được cách giải chính xác.
Ví dụ 1. {Chia thưởng) Cần chia hết m phần thưởng cho n học sinh sắp theo thứ tự từ giỏi trở xuống sao cho mỗi học sinh không nhận ít phần thưởng hơn bạn xếp sau mình.
Cho biết số cách chia hết m phần thưởng cho n học sinh.
Phân tích bài toán:
Gọi ChiaỤn, n) là số cách chia 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ưởng nà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 ít hơri số học sinh {m < n) thì từ học sinh thứ /?7 + 1 trở đi sẽ không được nhận phần thưởng nào, tức là phần thưẻfng chỉ có thể chia tối đa cho m học sinh: Chiơ{m, n) = Chiơ{m, m), nếu m < n.
1.4. Ta xét trườiig hợp m > n. Ta tách các phương án chia thành hai nhóm không giao nhau:
- Nhóm thứ nhất gồm các phương án trong đó học sinh thứ n không được nhận thưởng, tức là m phần thưởng chỉ chia cho n —1 học sinh và do đó số cách chia, tức là số phần tử của nhóm này sẽ là: Ơĩiơ(m, n -1).
- Nhóm thứ hai gồm các phương án mà học sinh thứ n cũng được nhận thưỏìig. Khi đó, do học sinh đứng cuối bảng thành tích được nhận phần thưởng thì mọi học sinh khác cũng sẽ có thưởng. Do ai cũng được thưỏỉng nên ta bớt của mỗi người một phần thưởng (để họ lĩnh sau), sô phần thưởng còn lại (m - n) sẽ được chia cho n học sinh. Sô cách chia khi đó sẽ là Chia{m - n, n).
Tổng số cách chia cho trường hợp m > n sẽ lầ tổng số phần tử của hai nhóm, ta có:
Chia{m, n) = Chia{m, n -1 ) + Chiaim - n, n).
Tổ chức dữ liệu và chương trình'. Ta có phương án 1 của Ihuật toán Chia như sau;
F u n c t i o n C h i a ( m , n : i n t e g e r ) : l o n g i n t ; B e g i n
i f m = 0 t h e n C h i a : = l e l s e {m>0}
i f n = 0 t h e n { m > 0 ; n = 0 } C h i a : = 0
e l s e { m , n > 0 }
i f m < n t h e n {OCm<n}
C h i a : = C h i a ( m , m ) e l s e {m>= n>0 }
C h i a : = C h i a ( m - n , n ) + C h i a ( m , n - 1 ) ; E n d ;
Phương án 1 khá dễ triển khai nhưng chưofng trình sẽ chạy rất lâu vì khi thực hiện sẽ sinh ra hiện tượng gọi lặp lại những hàm đệ quy đã tính ở các bước trước. Để tránh những rần gọi lặp như vậy chúng ta tính sẵn các giá trị của hàm theo các trị của đầu vào khác lĩhau và điền vào một mảng hai chiều cc. Mảng cc được mô tả như sau:
cc[ij] là số cách chia i phần thưỏfng cho j học sinh.
Theo phân tích của phương án 1, ta có:
- cc[0, 0] = 1; cx[/,0] = 0, với - cc[ij] = cc[i,i], nếu i <j.
- cc[ij] = cc[ij -1 ]+cc[i-jj], nếu i >= j.
166
Từ đó ta suy ra quy trình điền trị vào bảng cc như sau:
Khởi trị cột đầu tiên (tức cột 0) toàn 0. Riêng cc[0, 0]:= 1.
Lần lượt điền theo từng cột j-.=\..n. Tại mỗi cột j ta đặt:
cc[ớ'j]:= cc{i,ù] với i:=0..j -1 ;
cc[ij]:= c c [ i j - \ ]+cc[i-jJ] với i:=j..m.
Sau khi điền bảng, giá trị cc[m,n] chính là kết quả cần tìm.
F u n c t i o n C h i a 2 ( m , n : i n t e g e r ) : l o n g i n t ; v a r i , j : i n t e g e r ;
B e g i n
c c [ 0 , 0 ] : = 1 ;
{ C h i a 0 p h a n t h u o n g c h o 0 h o c s i n h } f o r i : = l t o m d o c c [ i , 0 ] ; = 0 ;
f o r j : = 1 t o n d o b e g i n
f o r i ; = 0 t o j - l d o c c [ i , j ] : = c c [ i , j - l ] ;
f o r i : = j t o m d o c c [ i , j ] : = c c [ i , j - l ] + C C [ i - j , j ] ; e n d ;
C h i a 2 : = c c [ m , n ] ; E n d ;
Việc dùng mảng hai chiều cc để lưu các kết quả trung gian làm hạn chế kích thước của bài toán có thể giải được bằng cách này. Ta có thể dùng một mảng một chiều thay thế cho mảng hai chiều với nhận xét; trong mảng hai chiều, cột thứ j có thể được tính toán từ cột thứ / -1. Nếu gọi c là mảng một chiều sẽ dùng, ta cho số học sinh tăng dần bằng cách lần lượt tính j bước, với j:=\..n. Tại bước thứ j, c[/] chính là số cách chia i phần thưởng cho j học sinh. Như vậy, tại bước thứ j ta có;
c[i] mới = 6'[/] cũ, nếu i < j. Từ đây suy ra đoạn c[0..ý—1] được bảo lưu.
c[/] mới = c[i] cũ + mới, nếu i > j.
Biểu thức thứ hai cho biết khi cập nhật mảng c từ bước thứ j -1 qua bước thứ j ta phải tính từ trên xuống, nghĩa là tính dần theo chiều tăng của i:= j..rn. Mảng c được khởi trị ở bước j = 0 như sau:
c[0] = l;c [/] = 0, v ớ i/:= l..m
với ý nghĩa, nếu có 0 học sinh thì chia 0 phần thưởng cho 0 học sinh sẽ được quy định là 1, nếu số phần thưỏng m khác 0 thì chia m phần thưởng cho 0 học sinh sẽ được 0 phương án.
Ta có phương án 3, dùng một mảng một chiều c như sau;
F u n c t i o n G h i a 3 ( m , n : i n t e g e r ) : l o n g i n t ; v a r i , j : i n t e g e r ;
B e g i n c [ 0 ] : = 1 ;
{ C h i a 0 p h a n t h u o n g c h o 0 n g u o i } f o r i : = l t o m d o c [ i ] : = 0 ;
f o r j : = 1 t o n d o
f o r i : = j t o m d o c [ i ] : = c [ i ] + c [ i - j ] ; C h i a 3 : = c [ m ] ;
E nd;
Vi dụ 2. Dãy con tăng dài nhất
Cho n là một số nguyờn dương và một dóy Oị, Oj,•••,ô„ cỏc số nguyờn. Hóy tỡm trong dãy đã cho một dãy con a,|, a,2,..., aII. không giảm và có số phần tử lớn nhất.
Ý tưỏfng của thuật toán quy hoạch động ở đây là để xây dựng dãy con không giảm dài nhất của dãy đã cho chúng ta sẽ xây dựng dãy con không giảm dài nhất của đoạn i phần tử đầu a^, a2,-:, a¡.
Để làm được điều đó: ta gọi S[i] là số phần tử của dãy con không giảm dài nhất, trong đó a, cũng thuộc dãy con trên và nó là phần tử cuối cùng.
Qiúng ta sẽ tính S[z'] ở từng bước dựa vào các giá trị tính được của các bước trước được lưu ỞS[i -1],..., S[l] như sau:
+ Ban đầu 5[/] với / = 1, 2,..., N được gán bằng 1 vì trưòíng hợp xấu nhất thì dãy cori chỉ là một phần tử.
+ Với mỗi i > 2 thì S[i] được tính bằng công thức truy hồi sau:
5[í]=Mữx{S[y‘] + l } với j = 1 mà aj< a¡.
Ví dụ
i 1 2 3 4 5 6 7 8 9 10 11 12
a[í] 6 12 8 11 . 3', ; 4' 1 7 5 9 10 2
Truocịi] 0 1 1 3 0 5 0 6 6 8 10 7
S[i] 1 2 2 3 1 2 1 3 3 4 5 2
L I A I I 1 V U W L U Ỉ Ỉ 1 W I 1 ỉ I C IỈ A I í , , ị 1 ỵ 't K JI y
nghĩa Truocịi^ là chỉ số của phần tử trước phần tử i trong dãy con cực đại lấy trong dãy
ữ \ , a 2 , . . . , ũ ị .
Thủ tục Find tính các giá trị cho mảng s và mảng Truoc, trong đó mảng s được khởi tạo là 1 và mảng Truoc được khởi tạo là 0.
168
P r o c e d u r e F i n d ; B e g i n
F o r i ; = 1 t o N d o b e g i n
S [ i ] : = l ; T r u o c [ i ] ; = 0 ; e n d ;
F o r i : = 2 t o N d o B e g i n
F o r j : = i - l d o w n t o 1 d o
I f ( A [ j ] < A [ i ] ) a n d ( S [ i ] < S [ j ] + 1 ) t h e n B e g i n
S [ i ] : = S [ j ] + l ; T r u o c [ i ] : = j ; E n d ;
E n d ; E n d ;
Bay gio chiing ta phai tim vi tri i sao cho S[/] dat max. Ta liiu vi tri do vao bien Luu.
S[Luu] chinh la so luong phan tir cua day con cuc dai ciia day da cho. Dua vao mang Truoc ta c6 the lay lai chi so cac phan tir thuoc day con do.
P r o c e d u r e O u t p u t R e s u l t ; B e g i n
L u u : = N ;
F o r i : = N - l d o w n t o 1 d o
I f S [ i ] > S [ L u u ] t h e n L u u : = i ; P r i n t ( L u u ) ;
E n d ;
Mang Truoc chi cho phep ta lln ngugc tir cuoi ve dau do do de in ra cac chi so theo thir tu tang dan ta phai dung them m6t mang phu P va in ngugc lai ciia mang Truoc hoac diing de quy de in. Thii tuc de quy Print in mang day con dai nhat ma chi so' cac phan tir dugc lay trong mkxxgTruoc nhu sau:
P r o c e d u r e P r i n t ( i :W o r d ) ; B e g i n
I f i >0 t h e n B e g i n
P r i n t ( T r u o c [ i ] ) ; W r i t e ( a [ i ] , ' ' ) ; E n d ;
E n d ;
Ví dụ 3. Dãy con chung dài nhất
Cho hai dãy số nguyên (ữ|,ữ2, . T ì m dãy con chung có độ dài lớn nhất của hai dãy trên (coi dãy không có số nguyên nào là dãy con của mọi dãy và có độ dài bằng 0).
Chúng ta có thể thấy ngay rằng độ phức tạp của bài toán trên phụ thuộc vào hai số m, n. Xét hai trường hợp;
+ Trường hợp ỉ . m = 0 hoặc n = 0.
Đây là trường hợp đặc biệt, có duy nhất một dãy con chung của hai dãy có độ dài bằng 0. Vì vậy dãy con chung có độ dài lớn nhất của chúng có độ dài bằng 0.
+ Trường hợp 2. m 0 và n ^ 0.
Trong trường hợp này, ta xét các bài toán nhỏ hơn là tìm dãy con chung có độ dài lớn nhất của hai dãy {bị,02,...,bị) với 0 < / < /77, 0 < ; < n. Gọi L[iJ] là độ dài của dãy con chung lớn nhất của hai dãy (a,,...,a,), (bị,...,bj). Như vậy ta phải tính tất cả các L[iJ]
trong âóO < i < m ,0 < j < n.
Chúng ta có thể thấy ngay rằng L[0, 0] = 0. Giả sử ta tính được L{s,t~\ với 1 < í < /,
\ <t <j.
- Nếu i < m ,j< n, a, ^ bj thì L[i,j}= max[L[i - 1J], L[iJ -1]}.
- Nếu i < m ,j < n, ũ ị = bị thì L[/, ý]= 1 + L[i -1 j -1 ].
Với những nhận xét trên, ta hoàn toàn tính được L[m,n\ chính là độ dài dãy con chung dài nhất của a„), {bị....b„).
Để tìm phần tử của dãy con, ta xuất phát từ ô Lịm, n] tới ô L[0, 0]. Giả sử ta đang ở ô L[/j]. Nếu a~hị thì ta thêm ữị vào dãy con rồi nhảy tới ô L[/ -1 j -1 ], Nếu a, it bj thì L[i,j] = L [ i- lJ ] hoặc L[i,j] = L[ỈJ -1], Nếu L[ij] = L[i-],j] thì nhảy tới ô L[i -1 j], ngược lại thì nhảy tới ô L[i,j-Ỉ].
Thủ tục Tinh thực hiện tính các giá trị cho mảng L như sau:
P r o c e d u r e T i n h ; B e g i n
L[0, 0]:=0;
F o r i : = 1 t o m d o F o r j : = 1 t o n d o
i f a t i ] o b [ j ] t h e n
L [ i , j ] : = m a x ( L [ i - l , j ] , L [ i , j - 1 ] ) e l s e L [ i , j ] : = L [ i - l , j - l ] + l ;
E nd ;
Thủ tục duyệt dựa vào mảng L để in dãy con chung dài nhất của a vầb:
1 7 0 22.CTDL rP M Ế M B
P r o c e d u r e D u y e t ; B e g i n
i : =m; j : = n ;
w h i l e ( i > 0 ) o r ( j > 0 ) d o i f a [ i ] = b [ j ] t h e n
b e g i n
w r i t e ( a [ i ] : 4 ) ; d e c ( i ) ; d e c ( j ) ; e n d
e l s e
i f L [ i , j ] = L [ i - 1 , j ] t h e n d e c ( i ) e l s e d e c ( j ) ;
E n d ;
Vi dụ 4. Bài toán cái túi
Một cái túi chứa được trọng lượng tối đa là M’. Có n đồ vật, đồ vật thứ i có khối lượng /;[/■] và giá trị C'[/], 1 < /■ < n. Tim cách xếp các đồ vật vào túi sao cho tổng giá trị các đồ vật trong túi là lớn nhất.
Gọi f(k,v) là giá trị lớn nhất của túi đựng trọng lượng V và chỉ chứa các đồ vật từ 1 đến k.
Nếu k = 1 thì f { k , v ) = (v div /:>[l])*c[l].
Giả sử tính được/(.y,/) với \ < s < k , 1 < t < V. Cần tính/(Ấ:,v).
Đặt: tg =V div p[k],f(k,v) = ma.\{f(k-\,u)+x*c[k] } (*) , với ,v = 0, 1,2,..., tg, u = v-x*p[k].
Giỗ trị lớn nhất là/(/7,M’).
Ta dùng mảng í , '[ l ..A7, l . . H ’] các bản ghi chứa kết quả trung gian. Mõi bản ghi ,ẹ[Ẩ:,v]
chứa giá trị f(k,v) và giá trị .V thoả mãn công thức (*).
Thủ tục Tinh dưới đây thực hiện tính các giá trị cho mảng g nói trên.
P r o c e d u r e T i n h ;
v a r i , ’j , k , m a x v a l , m a x n u m , g h : i n t e g e r ; B e g i n
f o r j : = l t o w d o f o r i : = 2 t o n d o
b e g i n
m a x v a l : = 0 ; g h : = j d i v p [ i ] ; f o r k : = 0 t o g h d o
i f g [ i - 1 , j - k * p [ i ] ] . v a l >= m a x v a l t h e n
b e g i n
m a x v a l : = g [ i - l , j - k * p [ i ] ] . v a l + k * c [ i ] ; m a x n u m : = k ;
e n d ;
g [ i , j ] . n u m ; =maxnum ; g [ i Õ ] ■ v a l : = m a x v a l ; e n d ;
E n d ;
Để xác định số lượng A'[/] đồ vật i thoả mãn điều kiện tối ưu, ta xuất phát từ a[n,w]
xỏc định được x[ô]. Chuyển tới a[n -\,w-x[n\*a{n]] xỏc định được x[n -1]. Cứ như vậy tớÌA'[l].
Thủ tục Duyet dưới đây xây dựng mảng x[\..n\, trong đ ó A'[/] là số lượng vật thứ i được sắp vào túi.
P r o c e d u r e D u y e t ;
v a r i , j : i n t e g e r ; B e g i n
i : = n ; j : =w ;
w h i l e ( i > = l ) a n d ( j > 0 ) d o b e g i n
x [ i ] : = g [ i , j ] . n u m ; j ; = j - x [ i ] * p [ i ] ; d e c ( i ) ;
e n d ;
_______ E nd;_____________________________________________________________
6. BÀI TẬ P
Bài 1. Dùng phưcfng pháp chia để trị xây dựng thuật toán và viết chương trình tính a" với a là số thực, n là số nguyên dương.
Bài 2. Dùng phương pháp chia để trị xây dựng thuật toán nhân hai số nguyên lớn.
Bài 3. Dùng phương pháp chia để trị xây dựng thuật toán tính min và max của một mảng nguyên /4[1
Bài 4. Hãy viết chương trình liệt kê tất cả các dãy nhị phân có độ dài n.
Bài 5. Cho ba kí tự A, B, c . Hãy tạo xâu có độ dài M < 250 chỉ chứa ba kí tự này và không có hai xâu con liền nhau giống nhau.
Bài 6. Cho một số nguyên dương M. Hãy tìm tất cả các cách phân tích M thành tổng các số nguyên dương nhỏ hofn M. Chú ý không xét đến các hoán vị của các số tìm được.
172
Bài 7. Xâu nhị phân là xâu chỉ chứa các kí tự 0 và 1. Xâu nhị phân s được gọi là không lặp bậc L nếu các xâu con có độ dài L của nó đều khác nhau từng đôi một. Xâu nhị phân không lặp bậc L được gọi là cực đại nếu việc bổ sung vào bên trái hoặc bên phải của xâu một kí tự 0 hoặc 1 thì sẽ phá vỡ tính không lặp bậc L. Viết chương trình xác định xâu nhị phân không lặp bậc L cực đại ngắn nhất.
Bài 8. Phương pháp quy hoạch động thường được dùng để giải các bài toán tối ưu thoả mãn nguyên lí tối ưu, phát biểu rằng, trong một dãy các quyết định hay lựa chọn tối ưu, mỗi một dãy con cũng phải là tối ưu.
Như đã biết, tích các ma trận có tính chất kết hợp, hãy dùng phương pháp quy hoạch động, xây dựng thuật toán tối ưu tính tích ma trận M = với số phép nhân phải thực hiện là ít nhất.
Bài 9. Cho G = {V, E) là một đồ thị có hướng, có trọng số, với n đỉnh được đánh số từ 1,2,..., n. Cung (/j) e E được gắn một trọng số L(/j) biểu diễn độ dài của cung (/,;).
Hãy dùng phưoíng pháp quy hoạch động, xây dựng một thuật toán tìm đường đi ngắn nhất từ đỉnh i tới đỉnh j với mọi cặp (/,ý).