Tải bản đầy đủ (.pdf) (19 trang)

Một số bài toán quy hoạch động code bằng python

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.39 MB, 19 trang )

CHUYÊN ĐỀ 9: QUY HOẠCH ĐỘNG

9.1. MỘT SỐ BÀI TOÁN BẮT ĐẦU
1. BÀI TOÁN FIBONACI
Dãy Fibonaci được định nghĩa như sau:
 F1  F2  1

 Fn  Fn 1  Fn  2 (n  3)
Em hãy tìm số Fibonaci thứ n
* Ý tưởng đầu tiên – Thuật toán 1: Đệ quy
Nhìn vào cơng thức trên ta thấy ngay bản chất đệ quy, việc tính Fn ta có thể viết ngay rất dễ
dàng và ngắn gọn:

Ta sẽ nhận xét về cách làm này thơng qua ví dụ sau:
Khi ta gọi: x:=Fb(10) thì việc tính fb(10) này sẽ tính như sau:
X:= fb(9) + fb(8)
X:= fb(7) + fb(8) + fb(6) + fb(7)
X:= fb(5) + fb(6) + fb(6) + fb(7) + fb(4) + fb(5) + fb(5) + fb(6)
…..
Ta thấy ngay bước thứ 3: có rất nhiều lời gọi hàm trùng lặp nhau: f6 gọi 3 lần, f5 gọi 3 lần … chính
điều này làm cho thời gian thực thi thuật toán này rất lớn. Chính vì vậy thuật tốn đệ quy này chỉ chạy
được với dữ liệu nhỏ. Với n>=44 thuật toán trên không khả thi.
* Ý tưởng cải tiến – Thuật toán 2: Quy hoạch động
Để thuật toán của ta chạy nhanh hơn ta thấy cần cải tiến làm sao để mỗi fb(x) chỉ cần tính một
lần.
Để làm điều này ta gọi F[i] là số fibonaci thứ i. (f là mảng một chiều)
Khi đó ta thấy việc tính mảng này rất nhanh chóng:

Trong thuật tốn trên: Việc tính các số fibonaci được lưu lại, mỗi số tính 1 lần, tính từ nhỏ đến lớn, Số
fibo sau được tính thơng qua các số fibo nhỏ hơn.  đó cũng là một phần của tư tưởng quy hoạch
động.


(chú thích: có các thuật tốn khác tốt hơn nữa để tính số fibonaci thứ n, tuy nhiên để làm quen
với tư tưởng QHĐ thì ta tạm dừng ở thuật tốn này)
2. BÀI TỐN SẮP BỊ

NGUỒN: NTU – TÊN BÀI: SABO

Anh nơng dân Bo có một đàn bò gồm rất nhiều con cái và con đực. Trong một hội chợ, anh
muốn sắp một hàng bò gồm n con. Tuy nhiên những con bò đực rất hung hăng nếu đứng gần nhau, anh
phải sắp tối thiểu k con bò cái xen giữa hai con bò đực để chúng khỏi húc nhau.
Bạn hãy giúp anh Bo đếm thử xem có bao nhiêu cách để sắp một hàng gồm n con bị mà hai
con bị đực bất kỳ khơng húc nhau (anh Bo có rất nhiều bị nên khơng sợ thiếu bị cái hoặc bị đực).
Ví dụ với n = 4 và k= 1, ta có 8 cách xếp như sau (M: bò đực, F: bò cái):
FFFF, MFFF, FMFF, FFMF, FFFM, MFMF, MFFM, FMFM.
Input


- Gồm hai số nguyên n và k cách nhau một khoảng trắng ( 1 ≤ n, k ≤ 1.000)
Output
- Số cách xếp hàng thỏa mãn yêu cầu. Do số lượng này có thể rất lớn nên chỉ cần in ra tối đa 6 chữ số
cuối cùng (modulo 1.000.000)
Ví dụ
Sapbo.inp
Sapbo.out
Sapbo.inp Sapbo.out
41
8
52
9
* ý tưởng đầu tiên: đệ quy quay lui
Ta nhận thấy bài này có thể quy về: Có bao nhiêu dãy nhị phân có độ dài n sao cho các kí tự

1 cách nhau k kí tự 0.
Ta sẽ duyệt quay lui tất cả cách sắp bò (sinh ra tất cả các dãy nhị phân có độ dài n thỏa mãn
điều kiện) và ta đếm.
Nhận xét: ở đây n<=1000 vậy cách liệt kê dãy nhị phân này không thể chạy được trong thời
gian cho phép.
* ý tưởng 2: Quy hoạch động
Ta gọi B[i] là số cách sắp i con bò thỏa mãn điều kiện của bài toán. Kết quả bài toán là B[n]
Vấn đề đặt ra ở bài này là ta chưa có cơng thức để tính b[i]. (Khơng giống bài 1: đã có cơng thức tính
F[i] = f[i-1] + f[i-2]).
Vậy để tính được mảng B này ta cần phải suy luận để tìm ra cơng thức. Trong những trường
hợp như vậy, cách làm chính là lấy giấy, bút, liệt kê thử xem với một số giá trị cụ thể:
Ví dụ: Với n=8 k=2 ta sẽ liệt kê thử số cách sắp:
Số cách sắp 1 con B[1] = 2 0 1
Số cách sắp 2 con B[2] = 3 00 01 10 (11 khơng được vì 2 bị đực sát nhau)
Số cách sắp 3 con B[3] = 4 000 001 010 100
Số cách sắp 4 con B[4] = 6 0000 0001 0010 0100 1000 1001
Số cách sắp 5 con B[5] = 9 00000 00001 00010 00100 01000 01001 10000 10001 10010
Số cách sắp 6 con B[6] = 13 000000 000001 000010 000100 …
Số cách sắp 7 con B[7] = 19 0000000 0000001 0000010 …
Số cách sắp 8 con B[8] = 28 00000000 00000010 00000100 …

Chắc chắn b[i] sẽ được tính thơng qua các b[..] nhỏ hơn nên ta thử:
B[1] = 1 + 1
B[2] = 2 + 1
B[3] = 3 + 1
B[4] = b[3] + b[1]
( 6 = 4 + 2)
B[5] = b[4] + b[2]
( 9 = 6 + 3)
B[6] = b[5] + b[3]


B[i] = b[i-1] + b[i-k-1]
 Đây chính là thứ ta cần tìm
Sau khi thử như trên ta có thể thấy được quy luật như sau:
Với i=1 đến k+1:
b[i] = i+1
Với i=k+2 đến n:
b[i] = b[i-1] + b[i-k-1]
Công thức ta vừa tìm được ở trên chính cơng thức quy hoạch. Chương trình áp dụng cơng thức trên
như sau:
fi = open("sapbo.inp","r")
fo = open("sapbo.out","w")
s = fi.read()
n, k = [int(x) for x in s.split()]
mod = 1000000
f = [ 0 for i in range(10001)]


f[1] = 2
for i in range(2, k+2): f[i] = i+1
for i in range(k+2,n+1): f[i] = (f[i-1]+f[i-k-1]) % mod
print(f[n],file = fo,end='')
fo.close()

3. MỘT SỐ BÀI TẬP MỞ ĐẦU LÀM QUEN VỚI QHĐ:
BÀI 9.1. 1. LÁT GẠCH VERSION 1
Nguồn: SPOJ – bài: LATGACH

Cho một hình chữ nhật kích thước 2xN (1<=N<=100). Hãy đếm số cách lát các viên gạch nhỏ kích
thước 1×2 và 2×1 vào hình trên sao cho khơng có phần nào của các viên gạch nhỏ thừa ra ngồi, cũng

khơng có vùng diện tích nào của hình chữ nhật khơng được lát.
Input
Gồm nhiều test, dòng đầu ghi số lượng test T ( T<=100 ).
T dòng sau mỗi dòng ghi một số N.
Output
Ghi ra T dịng là số cách lát tương ứng.
Ví dụ
Latgach1.inp Latgach1.out Latgach1.inp Latgach1.out
3
1
2
5
1
2
4
8
2
3
5
3
Gợi ý: F[i] là số cách lát gạch cho sân kích thức 2 x i
Cơng thức tính Fi  Fi 1  Fi  2
BÀI 9.1. 2. TỔNG ĐOẠN
Cho một dãy số nguyên A gồm n phần tử (ai<=10.000, n<=105). Nam thắc mắc liệu tổng của một đoạn
bất kỳ trong dãy số trên là bao nhiêu. Bạn hãy giúp cho Nam tính nhé!
Input
Dịng đầu ghi số lượng phần tử của dãy số N
Dòng 2: ghi các phần tử của dãy số A
Dòng 3: ghi số lượng test T ( T<=105).
T dòng sau mỗi dòng ghi hai số a và b (nam muốn tính tổng của đoạn [a..b]

Output Ghi ra T dòng tổng tương ứng của các bộ test
Ví dụ
Tongdoan.inp Tongdoan.out
3
4
135
9
2
12
13
Gợi ý: Nếu dùng cách duyệt bình thường O(n2) thì chắc chắn sẽ khơng chạy được với n  105 ; t  105
i

Gọi Fi   ak ; Khi đó tổng từ a đến b là Fb  Fa 1
k 1

BÀI 9.1. 3. HIỆU LỚN NHẤT: NGUỒN: NTU – BÀI: HISO
Cho một dãy n số nguyên a1, a2, ..., an. Hãy tìm hai chỉ số i, j sao cho i < j và hiệu aj - ai là lớn nhất.


Dữ liệu vào: gồm 2 dòng
- Dòng 1: là số nguyên n (2 ≤ n ≤ 105)
- Dòng 2: gồm n số nguyên a1, a2, ..., an (0 ≤ ai ≤ 109)
Dữ liệu xuất:
- Là giá trị lớn nhất của hiệu aj - ai.
Ví dụ
hieuso.inp
3
123


hieuso.out
2

hieuso.inp hieuso.out
4
3
2513

Cách 1: Duyệt với độ phức tạp O(n2), thực hiện được với n  105
Cách 2: Sử dụng thuật toán quy hoạch động sau đây
Gọi Fi là giá trị nhỏ nhất trong đoạn từ 1 đến i.
Khi đó để tìm giá trị lớn nhất từ i đến j ta sẽ duyệt như sau:
for i in range(2,n+1):
res = max(res, a[i] - F[i])

Kết quả là Res.
Chương trình:
fi = open("hieuso.inp","r")
fo = open("hieuso.out","w")
n = int(fi.readline())
a = [int(x) for x in fi.readline().split()]
F = [0 for i in range(100001)]
F[0] = a[0]
for i in range(1,n):
if F[i-1]F[i] = F[i-1]
else:
F[i] = a[i]
res = 0
for i in range(1,n):

if res < a[i] - F[i]:
res = max(res, a[i] - F[i])
print(res,file = fo, end='')
fo.close()
fi.close()

Cách 3:
Gọi min Fi là giá trị nhỏ nhất trong đoạn từ 1 đến i.
Gọi max Fi là giá trị lớn nhất trong đoạn từ i đến n.
Khi đó ta có res  max( res, max Fi  min Fi )
Chương trình
fi = open("hieuso.inp","r")
fo = open("hieuso.out","w")
n = int(fi.readline())
a = [int(x) for x in fi.readline().split()]
minF = [0 for i in range(100001)]
maxF = [0 for i in range(100001)]
minF[0] = a[0]
for i in range(1,n):
minF[i] = min(a[i], minF[i-1])
maxF[n-1] = a[n-1]


for i in range(n-1,0,-1):
maxF[i] = max(a[i], maxF[i+1])
res = 0
for i in range(n):
res = max(res, maxF[i] - minF[i])
print(res,file = fo, end='')
fo.close()

fi.close()

4.4 LÁT GẠCH VERSION 2: NGUỒN NTU – BÀI: LAGA
Có một khoảng sân hình chữ nhật kích thước 2 x n ô vuông, gồm 2 hàng và n cột. Đánh số hàng từ 1
đến 2 theo thứ tự từ trên xuống dưới, đánh số cột từ 1 đến n theo thứ tự từ trái qua phải. Người ta muốn
lát sân bằng gạch màu trắng và điểm xuyết một số ô gạch màu đen, mỗi ô vuông được lát bởi một viên
gạch, sao cho khơng có hai viên gạch màu đen nào chung cạnh với nhau. Hỏi có tất cả bao nhiêu cách
khác nhau để lát khoảng sân trên (hai cách lát sân được gọi là khác nhau nếu tồn tại tối thiểu một ơ ở
dịng i cột j được lát gạch màu trắng ở cách này và lát gạch màu đen ở cách kia).
Ví dụ với n = 2, ta có 7 cách lát sân sau đây:

Input : Một số nguyên n (1 ≤ n ≤ 1000)
Output Số cách lát gạch khoảng sân theo yêu cầu trên. Số lượng này có thể rất lớn nên chỉ cần in ra 8
số cuối (mod 100.000.000)
Ví dụ
Laga.inp Laga.out Laga.inp Laga.out Laga.inp Laga.out
2
7
3
17
4
41
Hướng dẫn
F0  0 ; 𝐹 = (𝐹 + 2 ∗ 𝐹 ) % 1 0
Kết quả: Fn . Độ phức tạp: O(n)
4.5. DẠO CHƠI BẰNG XE BUÝT
Nguồn: SPOJ – Bài: KMBUS
Một tuyến đường ở thành phố có các bến xe bus ở từng km tuyến đường. Mỗi lần qua bến, xe
đều đỗ để đón khách. Mỗi bến đều có điểm xuất phát. Một xe chỉ chạy không quá B km kể từ điểm
xuất phát của nó. Hành khách khi đi xe sẽ phải trả tiền cho độ dài đoạn đường mà họ ngồi trên xe.

Cước phí cần trả để đi đoạn đường độ dài i là Ci (i=1,2..B). Một du khách xuất phát từ 1 bến nào đó
muốn đi dạo L (km) theo tuyến nói trên. Hỏi ơng ta phải lên xuống xe như thế nào để tổng số tiền phải
trả là nhỏ nhất có thể.
Dữ liệu vào
Dịng đầu ghi 2 số nguyên dương B, L.
Dòng thứ i trong số B dòng tiếp theo ghi 1 số nguyên dương Ci ( 1 ≤ i ≤ B ).
Kết qủa
Một dòng duy nhất là số tiền nhỏ nhất phải trả
Giới hạn
0 ≤ B ≤ 100 0 ≤ L ≤ 10000
0 ≤ Ci ≤ 100
Ví dụ
KMBUS
KMBUS
57
14
3
4
6
9


22
Công thức QHĐ: F [0]  0; F [i ]  min( F [i ], F [ j ]  C[i  j ]);i  1, 2,..., n; j  max(0,i  B),...,i 1
Nếu tìm đường đi thì ta truy vết như sau:
I = L;
while i>0:
i = i-F[i];
print(i,end=' ');


9.2. QUY HOẠCH ĐỘNG VỚI MẢNG 2 CHIỀU
1. BÀI TOÁN: XÂU CON CHUNG DÀI NHẤT
Bài toán: Cho hai xâu X và Y. Hãy tìm xâu con chung dài nhất (LCS) của hai xâu X và Y. Xâu
con chung là xâu khi xóa đi một số kí tự của hai xâu thì hai xâu cịn lại của chúng giống nhau.
Ví dụ: X = 'CEACEEC'
Y = 'AECECA'
Xâu con chung: ECEC, AEEC
Thuật toán:
Đặt các chuỗi đầu vào lần lượt là X[0..m-1] và Y[0..n-1] có độ dài m và n.
Và đặt L(X[0..m-1], Y[0..n-1]) là độ dài LCS của hai dãy X và Y.
Sau đây là định nghĩa đệ quy của 𝐿(𝑋[0. . 𝑚 − 1], 𝑌[0. . 𝑛 − 1]).
 Nếu các ký tự cuối cùng của cả hai chuỗi khớp nhau (hoặc X[m-1] == Y[n-1]) thì:
L(X[0..m-1], Y[0..n-1]) = 1 + L(X[0..m-2], Y[0..n-2])
 Nếu các ký tự cuối cùng của cả hai chuỗi không khớp (hoặc X[m-1] != Y[n-1]) thì:
L(X[0..m-1], Y[0..n-1]) = max( L(X[0..m-2], Y[0..n-1]), L(X[0..m-1], Y[0..n-2]) )
Ví dụ:
1) Xem xét chuỗi đầu vào "AGGTAB" và "GXTXAYB". Các ký tự cuối cùng khớp với các
chuỗi. Vì vậy độ dài của LCS có thể được viết là:
L("AGGTAB", "GXTXAYB") = 1 + L("AGGTA", "GXTXAY")

2) Xem xét các chuỗi đầu vào "ABCDGH" và "AEDFHR". Các ký tự cuối cùng khơng khớp với
chuỗi. Vậy độ dài của LCS có thể được viết là:
L("ABCDGH", "AEDFHR") = max( L("ABCDG", "AEDFHR"), L("ABCDGH", "AEDFH") )
Vậy bài tốn LCS có thuộc tính cấu trúc con tối ưu vì vấn đề chính có thể được giải quyết bằng cách
sử dụng các giải pháp cho các vấn đề con.
Từ đó ta có cơng thức Quy hoạch động như sau:
Khi áp dụng QHĐ để giải bài này ta chú ý xây dựng công thức như sau:


Gọi 𝐿[𝑖][𝑗] là độ dài dãy con chung dài nhất của 2 dãy X[0..i-1] và b[0..j-1]. Khi đó ta có:

 Nếu 𝑖 = 0, 𝑗 = 0 thì 𝐿[𝑖][𝑗] = 0
 Nếu 𝑎[𝑖 − 1] ! = 𝑏[𝑗 − 1] thì 𝐿[𝑖][𝑗] = 𝑀𝑎𝑥( 𝐿[𝑖 − 1][𝑗] , 𝐿[𝑖][𝑗 − 1])
 Nếu 𝑎[𝑖 − 1] = 𝑏[𝑗 − 1] thì 𝐿[𝑖][𝑗] = 1 + 𝐿[𝑖 − 1][𝑗 − 1]
Kết quả của bài toán Độ dài xâu con chung dài nhất là 𝐿[𝑚][𝑛]
Đoạn code:
def LCS(a,b):
for i in range(m+1):
for j in range(n+1):
if i==0 or j==0:
F[i][j] = 0
elif a[i-1] == b[j-1]:
F[i][j] = F[i-1][j-1] + 1
else:
F[i][j] = max(F[i-1][j], F[i][j-1])
return F[m][n]

Để truy vết kết quả xâu con chung ta sẽ truy vết dựa vào mảng L: Đi từ ô L[m][n] về ô L[0][0]
def trace(a,b):
i= m
j = n
res =''
while i>0 and j>0:
if a[i-1] ==b[j-1]:
res = a[i-1]+res
i -= 1
j -= 1
elif F[i][j] == F[i-1][j]:
i -= 1
else:
j -= 1

return res

BÀI TẬP ÁP DỤNG
BÀI 9.2. 1. XÂU CON CHUNG DÀI NHẤT (QBSTR)
Nguồn bài: />
Xâu kí tự X được gọi là xâu con của xâu kí tự Y nếu ta có thể xố đi một số kí tự trong xâu Y để
được xâu X. Cho biết hai xâu kí tự A và B, hãy tìm xâu kí tự C có độ dài lớn nhất và là con của cả A
và B.
Dữ liệu vào:
+ Dòng thứ nhất chứa xâu A
+ Dòng thứ hai chứa xâu B.
Dữ liệu ra:
+ Dòng 1: chứa số nguyên là độ dài lớn nhất của xâu con tìm được.
+ Dịng 2: chứa xâu con chung dài nhất của hai xâu A và B.
Ví dụ:
Input
Output
abc1def2ghi3
10
abcdefghi123
abcdefghi3
Cách 1: Giải bằng CT ở phần lí thuyết
Như vậy độ phức tạp bộ nhớ của bài toán là O(n2), độ phức tạp thời gian là O(n2).


Có một phương pháp cài đặt tốt hơn, chỉ với độ phức tạp bộ nhớ O(n) dựa trên nhận xét sau: để tính
ơ L[i][j] của bảng phương án, ta chỉ cần 3 ơ L[i−1][j−1], L[i−1][j] và L[i][j−1].
Tức là để tính dịng L[i] thì chỉ cần dịng L[i−1]. Do đó ta chỉ cần 2 mảng 1 chiều để lưu dịng vừa
tính (P) và dịng đang tính (L) mà thơi. Cách cài đặt mới như sau:
Cách 2:

Ta sử dụng 2 mảng một chiều P và L, trong đó P là mảng đã tính ở bước thứ i–1, L là mảng tính ở
bước thứ i. Ta có, tại bước i, ta xét kí tự x[i-1], với mỗi j = 1… n.
 Nếu x[i] = y[j] thì L[j] = P[j–1] + 1;
 Nếu x[i] ≠ y[j] thì L[j] = max(P[j], L[j–1]).
a
b
m
n
L

=
=
=
=
=

input()
input()
len(a)
len(b)
[0 for i in range(1001)]

def LCS(a,b):
P = [0 for i in range(1001)]
for i in range(m+1):
for j in range(n+1):
if j==0:
L[j] = 0
elif a[i-1] == b[j-1]:
L[j] = L[j-1] + 1

else:
L[j] = max(P[j], L[j-1])
P = L.copy()
return L[n]
def trace(a,b):
i= m
j = n
res =''
while i>0 and j>0:
if a[i-1] ==b[j-1]:
res = a[i-1]+res
i -= 1
j -= 1
elif L[j] == L[j-1]:
j -= 1
else:
i -= 1
return res
print(LCS(a,b))
print(trace(a,b))

Bài tập tương tự:
Cho hai xâu x gồm m và y gồm n kí tự. Cần xóa đi từ xâu x, dx kí tự và từ xâu y, dy kí tự để
thu được hai xâu giống nhau. Hãy xác định giá trị nhỏ nhất của tổng dx+dy.
Thuật toán:
k = F[m,n];
dx = length(x) – k;
dy = length(y) – k;
 dx  dy  max
(Lưu ý: Đối với bài toán cho hai dãy số nguyên ta vẫn áp dụng thuật tốn tương tự, thay vì xử lí các

kí tự, ta sẽ xử lí từng phần tử của mảng)


BÀI 9.2. 2. ĐOẠN CHUNG
Hãy tìm chiều dài lớn nhất k trong số các đoạn chung của hai xâu x và y.
Thí dụ, x = "xabcxxabcdxd", y = "aybcyabcdydy" có chiều dài của đoạn chung dài nhất là 4 ứng với
đoạn "abcd".
Input:
+ Dòng 1 chứa xâu x;
+ Dòng 2 chứa xâu y;
Output: nguyên k là độ dài đoạn chung tìm được
Ví dụ:
Input
Output
xabcxxabcdxd
4
aybcyabcdydy
Thuật tốn.
Dùng mảng hai chiều L[i][j] là chiều dài lớn nhất của hai đoạn giống nhau 𝑥[𝑖 − 𝑘. . 𝑖 − 1] và
𝑦[𝑗𝑘. . 𝑗 − 1], sao cho k  max. Ta có,
 Nếu 𝑥[𝑖 − 1] = 𝑦[𝑗 − 1] thì 𝐿[𝑖][𝑗] = 𝐿[𝑖– 1][𝑗– 1] + 1;
 Nếu 𝑥[𝑖 − 1] ≠ 𝑦[𝑗 − 1] thì 𝐿[𝑖][𝑗] = 0.
Chiều dài đoạn con chung dài nhất sẽ là Max{L[i][j] | 1  i  len(x), 1  j  len(y)}.
Các bài tương tự
1. Đoạn chung 2. Cho xâu x gồm m kí tự và xâu y gồm n kí tự. Tìm đoạn chung dài nhất của hai xâu
này. Kết quả cho ra 4 giá trị dx, cx, dy, cy, trong đó x[dx..cx] = y[dy..cy] là hai đoạn tìm được.
Yêu cầu: Kết quả ghi ra file "DCHUNG.OUT" gồm một dòng chứa 4 số nguyên tương ứng với dx,
cx, dy, cy. Mỗi số cách nhau ít nhất một dấu cách.
Bài toán mở rộng: yêu cầu ghi ra các phần tử của đoạn chung.
Ví dụ:

Input
Output
xabcxxabcdxd
4
aybcyabcdydy
abcd
Thuật tốn bài đoạn chung 2:
Khi phát hiện a[j] > kmax ta ghi nhận imax = i; jmax = j; kmax = k. Cuối thủ tục ta tính cx = imax;
dx = cx–kmax+1; cy = jmax; dy = cy–kmax+1.
2. Đoạn chung 3. Cho hai dãy số nguyên a gồm m và b gồm n phần tử. Xác định chiều dài lớn nhất k
để hai dãy cùng chứa k phần tử liên tiếp như nhau: a[i] = b[j], a[i+1] = b[j+1],…,a[i+k–1] = b[j+k–1].
(Lưu ý: Thuật toán xử lí trên mảng số nguyên, cách làm tương tự bài đoạn chung.)
BÀI 9.2. 3. DÃY CON CHUNG DÀI NHẤT
(Nguồn bài: Câu 3,Quảng Bình 2012-2013)
Cho dãy số nguyên A gồm N phần tử a1, a2, ..., aN và dãy số nguyên B gồm M phần tử b1, b2, ..., bM.
Các phần tử trong một dãy số có giá trị khác nhau từng đôi một. (1 ≤ ai, bj ≤ 2x109; 1 ≤ N ≤ 100; 1 ≤ i ≤
N; 1 ≤ M ≤ 100; 1 ≤ j ≤ M). Dãy C được gọi là dãy con của dãy A nếu dãy C nhận được từ dãy A bằng
cách xóa đi một số phần tử và giữ nguyên thứ tự của các phần tử còn lại. Nếu dãy C là dãy con của dãy
A và cũng là dãy con của dãy B thì dãy C được gọi là dãy con chung của hai dãy A và B.
Yêu cầu: Hãy tìm dãy C là dãy con chung của hai dãy A và B sao cho số lượng phần tử của dãy C là
lớn nhất.
Dữ liệu vào:
- Dòng 1: Ghi số nguyên dương N là số lượng phần tử của dãy A.


- Dòng 2: Ghi N số nguyên là giá trị của các phần tử trong dãy A, các số được ghi cách nhau ít
nhất một dấu cách.
- Dịng 3: Ghi số nguyên dương M là số lượng phần tử của dãy B.
- Dòng 4: Ghi M số nguyên là giá trị của các phần tử trong dãy B, các số được ghi cách nhau
ít nhất một dấu cách.

Kết quả ra:
- Dòng 1: Ghi số nguyên dương K là số lượng phần tử của dãy C.
- Dòng 2: Ghi K số nguyên là giá trị của các phần tử trong dãy C, các số được ghi cách nhau
một dấu cách.
- Dòng 3: Ghi K số nguyên dương lần lượt là chỉ số của các phần tử trong dãy A tương ứng với
các giá trị của phần tử đó trong dãy C, các số được ghi cách nhau một dấu cách.
- Dòng 4: Ghi K số nguyên dương lần lượt là chỉ số của các phần tử trong dãy B tương ứng với
các giá trị của phần tử đó trong dãy C, các số được ghi cách nhau một dấu cách.
Ví dụ:
Input
6
9 3 1 12 6 15
5
3 12 7 6 15

Output
4
3 12 6 15
2456
1245

BÀI 9.2. 4. BLGEN - CHUỖI GEN ĐẶC TRƯNG
Tế bào của một cá thể sinh vật ngoài hành tinh mới được phát hiện gồm rất nhiều gen, mỗi gen
trong chuỗi gen của tế bào đều có số lượng nào đó các nucleotide (ký hiệu là nu). Các chuyên
gia thường quan tâm chuỗi gen của mỗi cá thể dưới góc độ một chuỗi số lượng tương ứng các nu gọi
tắt là chuỗi nu), do đó chuỗi sẽ như là một dãy số nguyên dương đồng thời số số hạng của dãy này
sẽ được gọi là độ dài của chuỗi. Mỗi gen được xem là đặc biệt nếu số nu của nó hoặc là bình phương
của một số ngun hoặc là lập phương của một số nguyên tố.
Để nghiên cứu khả năng biến đổi gen của lồi sinh vật nói trên, các nhà khoa học xem xét hai mẫu
chuỗi nu của hai cá thể và quan tâm đến mức độ "giống nhau" giữa chúng theo cách tìm ra chuỗi con

chỉ gồm các gen đặc biệt mà cùng xuất hiện ở cả hai chuỗi nu (mỗi chuỗi con như vậy đều được gọi là
chuỗi đặc trưng chung của hai chuỗi nu). Lưu ý rằng, chuỗi con của một chuỗi nu X, là chuỗi thu được
từ X bằng cách giữ nguyên tất cả hoặc loại bỏ đi một số nào đó các gen mà vẫn giữ thứ tự xuất hiện
trong chuỗi X.
Yêu cầu: Xác định độ dài lớn nhất L của chuỗi đặc trưng chung của hai chuỗi nu cho trước.
Dữ liệu:
 Dòng đầu ghi lần lượt các số hạng của chuỗi nu thứ nhất.
 Dòng tiếp theo ghi lần lượt các số hạng của chuỗi nu thứ hai.
 Tất cả các số hạng của hai chuỗi đều nguyên dương và không vượt quá 1019
(Độ dài của mỗi chuỗi nu đều không vượt quá 1000)
Kết quả: Ghi ra file văn bản GEN.OUT duy nhất một số ngun L tìm được.
Ví dụ:
Input
Output
2 9 8 4 1 27 4 6
4
5 6 9 1 8 2 6 27 1 4
(Giải thích: L = 4, một trong các chuỗi đăc trưng chung là: 9, 1, 27, 4)
Ràng buộc: 60% số test ứng với 60% số điểm của bài ứng với tình huống độ dài của hai chuỗi
nu không vượt quá 255 và giá trị của mỗi số hạng đều không vượt quá 106
.


2. BÀI TỐN CON KIẾN:
Có một chú kiến bị đi kiếm ăn trên sân trường. Sân trường có kích thước MxN và được chia thành m
hàng n cột đều nhau.
Mỗi ô trên sân trường có chứa một số lượng thức ăn nhất định. Chú kiến có thể xuất phát vào
một ô bất kỳ của cột 1 và muốn bò hết sang cột n. Với mỗi bước đi thì chú kiến chỉ có thể bị sang 1
trong 3 ơ kề của cột bên cạnh.
K

Bạn hãy giúp cho chú kiến tìm một đường đi từ cột 1 sang cột n sao cho lượng thức ăn mà chú
ăn được trên đường đi là nhiều nhất.
Hướng giải quyết: Vì theo yêu cầu thì mỗi bước đi nó có thể sang 3 ơ của cột kế tiếp như hình
vẽ trên. Vậy ta áp dụng QHĐ vào đây như sau: Ta xây dựng bảng phương án L(i,j) là lượng thức ăn
mà kiến thu được lớn nhất khi bị đến ơ (i,j). Vậy thì ta có Kết quả sẽ là Max của cột cuối cùng – Cột
n. Vậy từ đây ta có cơng thức tính bảng L như sau:
Ban đầu L = 0 Với mọi i,j (Sử dụng Fillchar)
L[i,1] = C[i,1]
{Vì kiến xuất phát từ cột 1}
L[i][j] = C[i,j] + Max (của 3 ơ trước nó tức: L[i-i,j-1], L[i,j-1], L[i+1,j-1])
L[i-1,j-1]
L[i,j-1]
L[i][j]
L[i+1,j-1]
Hình 1: Để đến được ơ L[i][j] nó chỉ có thể đến từ 3 ơ phía sau như bảng trên.
Để truy ra kết quả đường đi: dựa vào bảng phương án L này
BÀI TẬP ÁP DỤNG
BÀI 9.2. 5. SA MẠC
Sa mạc là lưới ô vuông cấp MxN ( 1≤ N,M ≤ 100). Trên mỗi ô của lưới người ta ghi một số
nguyên a ( 1≤ a ≤ 100) được gọi là năng lượng của ơ đó. Một con lạc đà đang ở ô (i,j) của lưới chỉ
được đi đến một trong hai ô (i+1,j) hoặc ô (i,j+1). Lạc đà đi đến ơ nào thì hấp thụ được nguồn năng
lượng tại ơ đó. Hãy tìm cho lạc đà một đường đi từ ô (1,1) đến ô (M, N) theo nguyên tắc trên và hấp
thụ được nhiều năng lượng nhất.
Dữ liệu vào: Từ file văn bản SAMAC.INP, dòng đầu tiên ghi 2 số nguyên dương theo thứ tự
M, N.
Dòng thứ i trong M dòng tiếp theo ghi N số nguyên dương, số thứ j là năng lượng trên ô (i , j)
của sa mạc (số thứ tự của các số trên một dịng tính từ trái qua phải)
Kết quả: Ghi ra file văn bản SAMAC.OUT, dòng đầu tiên ghi số S là năng lượng mà lạc đà
hấp thụ được. Từ dòng thứ 2 trở đi mỗi dòng ghi 2 số nguyên dương là toạ độ các ô theo thứ tự trên
đường đi của lạc đà.


Ví dụ:
SAMAC.INP
45
15134
67915
11841
13433

SAMAC.OUT
41
11
21
22
23
33


34
44
45
Cả hai file dữ liệu, các số trên một dòng cách nhau một kí tự trắng.
Thuật tốn:
 Khởi tạo: 𝐿[𝑖][𝑗] = 0, 𝑛ế𝑢 𝑖 = 0 ℎ𝑜ặ𝑐 𝑗 = 0
 QHĐ: 𝐿[𝑖][𝑗] = 𝐶[𝑖 − 1][𝑗 − 1] + 𝑚𝑎𝑥(𝐿[𝑖 − 1][𝑗], 𝐿[𝑖][𝑗 − 1]) với C[i-1][j-1] là giá trị
tại ô (i,j) với 𝑖 = 1,2, … , 𝑚 và 𝑗 = 1,2, … , 𝑛
fi = open('samac.inp','r')
fo = open('samac.out','w')
#nhap
m,n = [int(x) for x in fi.readline().split()]

C = []
for i in range(m):
s = [int(x) for x in fi.readline().split()]
C.append(s)
L = [[0 for j in range(n+1)] for i in range(m+1)]
# QHD
def QHD():
for i in range(m+1):
for j in range(n+1):
if i==0 or j==0:
L[i][j] =0
else:
L[i][j] = C[i-1][j-1] + max(L[i1][j],L[i][j-1])
return L[m][n]
#trace
def Trace():
i=m
j=n
s = []
while i>0 and j>0:
s1= str(i)+' '+ str(j)
#print(s1)
s.append(s1)
if L[i][j] == L[i-1][j] + C[i-1][j-1]:
i -= 1
else:
j -= 1
for i in s[::-1]:
print(i,file = fo)
return 0

#main
print(QHD(),file=fo)
Trace()
fo.close()
fi.close()

BÀI 9.2. 6. CON THẠCH SÙNG
Sau trận mưa tối hôm qua, một bức tường trong nhà có rất nhiều muỗi đậu trên đó. Bức tường
có kích thước h x w ơ vng, trong đó h là số hàng được đánh số từ 1 đến h theo chiều từ trên xuống
dưới và w là số cột được đánh số từ 1 đến w theo chiều từ trái qua phải. Tại mỗi ô vuông đã có khoảng
từ 1 đến 1000 con muỗi đậu ở đó. Một con thạch sùng muốn ăn nhiều nhất muỗi có thể, tùy thuộc vào
hạn chế như sau: Nó bắt đầu chọn một ô nhiều muỗi nhất ở hàng trên cùng và ăn số muỗi tại đây. Sau


đó chuyển xuống một ơ vng ở hàng bên dưới, tiếp tục ăn muỗi tại đó, con thạch sùng cứ chuyển như
vậy đến hết hàng ngang cuối cùng để ăn số muỗi trên mỗi ô. Khi chuyển xuống ô tiếp theo của hàng
dưới, nó có thể chuyển theo một trong 3 hướng như hình vẽ.

Yêu cầu: Cho h và w và số muỗi trong mỗi ơ, con thạch sùng có thể ăn nhiều nhất là bao nhiêu
con muỗi trong một lần duy nhất di chuyển từ hàng trên cùng xuống hàng cuối cùng?
Dữ liệu vào: Dòng đầu ghi hai số nguyên, số đầu tiên là h – số hàng, số nguyên thứ hai là w –
số cột. Dòng thứ i trong h dòng tiếp theo ghi w số nguyên m là số muỗi trong mỗi ô. Tất cả các số
nguyên đều cách nhau một dấu cách.
Dữ liệu ra: Một dòng duy nhất ghi một số nguyên duy nhất là số muỗi lớn nhất mà con thạch
sùng có thể ăn được trong một lần di chuyển duy nhất từ hàng trên cùng xuống hàng dưới cùng.
Ví dụ:
Input Output
65
32
31742

21311
12218
22153
21444
57251
Hạn chế:
1 ≤ h ≤ 500.
1 ≤ w ≤ 500.
1 ≤ m ≤ 1000.
Giải thích ví dụ: Con thạch sùng chọn như sau: hàng 1 chọn 7, hàng 2 chọn 1, hàng 3 chọn 8,
hàng 4 chọn 5, hàng 5 chọn 4, hàng 6 chọn 7. Tổng: 7 + 1 + 8 + 5 + 4 + 7 = 32.
BÀI 9.2. 7. ROBOT
Cho hình chữa nhật NxM ơ vng (N dịng, M cột). Mỗi ô vuông ghi một số nguyên có giá trị
không vượt quá 100 thể hiện cho mức độ cản trở Robot vào ơ đó. Một Robot đứng tại ơ góc trái trên
(1,1) muốn di chuyển xuống góc phải dưới (N, M) của hình chữ nhật. Mỗi bước Robot di chuyển sang
bên phải hoặc ơ phía dưới ơ đang đứng.
u cầu: Tìm con đường ít cản trở Robot nhất và tính tổng các mức cản trở Robot trên con đường đó
(kể cả mức cản trở tại ô đầu tiên Robot đang đứng)
Dữ liệu vào: Tệp ROBOT.INP
- Dòng đầu chứa hai số nguyên N, M 1  N  100,1  M  100 
- N dịng sau, mỗi dịng có M số nguyên thể hiện các mức cản trở Robot tại NxM ô vuông.
Dữ liệu ra: Tệp ROBOT.OUT ghi một số nguyên thể hiện tổng mức cản trở Robot ít nhất.
Ví dụ
ROBOT.INP ROBOT.OUT
34
8
1111
5 2 2 100
9421
BÀI 9.2. 8. BÀI TOÁN BÀN CỜ

Xét một bàn cờ hình vng kích thước nxn với mỗi ô c[i,j] là số lượng hạt đậu đang nằm ở ô [i,j]. Một
quân cờ xuất phát từ một ô bất kỳ của hàng 1, ta cần tìm một đường đi đến hàng cuối cùng n sao cho
số lượng hạt đậu quân cờ nhặt được trên đường đi là lớn nhất.


Quân cờ chỉ có thể đi thẳng xuống, chéo xuống sang trái hoặc chéo xuống sang phải. quân cờ
đi qua ơ nào thì có thể nhặt được số lượng các hạt đậu trên ơ này. (n<=1000)
Ví dụ: với n=3, đáp án: 17
4
5
2
5
2
3
1
7
4
Một số bài tương tự:
Mã bài: QBMAX
Mã bài: GIAN

– Địa chỉ: />– Địa chỉ: />
3. BÀI TOÁN TAM GIÁC SỐ

Tính tổng lớn nhất trên đường đi từ đỉnh xuống đáy của tam giác. Với mỗi bước đi có thể đi
chéo sang trái hoặc chéo sang phải.
Nếu nhìn tam giác số trên theo mảng 2 chiều sẽ dễ nhìn hơn:
7
3
5

8
1
0
2
7
4
4
4
5
2
6
5
Vậy sẽ quy về giống bài toán con kiến: Đi từ ô (1,1) xuống đáy.
𝑳[𝒊][𝒋] = 𝑪[𝒊][𝒋] + 𝒎𝒂𝒙(𝑪[𝒊 − 𝟏][𝒋 − 𝟏], 𝑪[𝒊 − 𝟏][𝒋] )
Kết quả tổng lớn nhất sẽ là max của dòng cuối cùng m.
Nếu yêu cầu kết quả cần có thêm đường đi đã qua những ơ nào (7, 3, 8, 7, 5) thì có thể try vết
dựa vào bảng phương án L.

Bảng C

Bảng L

4. BÀI TỐN Cnk
Tính Ckn .
Chúng ta đã biết các cơng thức sau:
Ckn = 1
(Với k=0 hoặc k=n)
Ckn = Ckn-1 + Ck-1n-1 (Với 0Ta có thể viết chương trình con đệ quy để tính Ckn nhưng hiệu quả khơng cao vì thời gian thực
hiện sẽ rất lớn.

Áp dụng QHĐ: Ta xây dựng bảng phương án C để lưu.
0
1
2
..
n
0
1
1
1
1
2
1
2
1
..
k
C[k][n]
Vậy thì ta có cơng thức tính bảng C:


 𝐶[𝑖][0] = 1 với 𝑖 = 1,2, … , 𝑘
 𝐶[𝑖][𝑗] = 𝐶[𝑖][𝑗 − 1] + 𝐶[𝑖 − 1][𝑗 − 1] với 𝑖 = 1,2, … , 𝑚 và 𝑗 = 1,2, … , 𝑖
Kết quả của bài toán sẽ là 𝐶[𝑘][𝑛]
5. BẮC CẦU (TƯƠNG TỰ BÀI XÂU CON CHUNG)
Hai nước Anpha và Beta nằm ở hai bên bờ sơng Omega, Anpha nằm ở bờ bắc và có M thành
phố được đánh số từ 1 đến m, Beta nằm ở bờ nam và có N thành phố được đánh số từ 1 đến n (theo vị
trí từ đơng sang tây).
Mỗi thành phố của nước này thường có quan hệ kết nghĩa với một số thành phố của nước kia.
Để tăng cường tình hữu nghị, hai nước muốn xây các cây cầu bắc qua sông, mỗi cây cầu sẽ là nhịp cầu

nối 2 thành phố kết nghĩa. Với yêu cầu là các cây cầu không được cắt nhau và mỗi thành phố chỉ là
đầu cầu cho nhiều nhất là một cây cầu, hãy chỉ ra cách bắc cầu được nhiều cầu nhất.
Hướng dẫn: Gọi các thành phố của Anpha lần lượt là a1,a2,…am; các thành phố của Beta là
b1,b2,...bn. Nếu thành phố ai và bj kết nghĩa với nhau thì coi ai "bằng" bj. Để các cây cầu không cắt
nhau, nếu ta đã chọn cặp thành phố (ai,bj) để xây cầu thì cặp tiếp theo phải là cặp
(au,bv) sao cho u>i và v>j. Như vậy các cặp thành phố được chọn xây cầu có thể coi là một dãy con
chung của hai dãy a và b.
Bài toán của chúng ta trở thành bài tốn tìm dãy con chung dài nhất, ở đây hai phần tử "bằng"
nhau nếu chúng có quan hệ kết nghĩa.

9.3. LỚP BÀI TỐN CÁI TÚI
1. Mơ hình
Trong siêu thị có n đồ vật (n≤1000), đồ vật thứ i có trọng lượng là W[i]≤1000 và giá trị V[i]
≤1000. Một tên trộm đột nhập vào siêu thị, tên trộm mang theo một cái túi có thể mang được tối đa
trọng lượng M (M≤1000). Hỏi tên trộm sẽ lấy đi những đồ vật nào để được tổng giá trị lớn nhất.
Giải quyết bài toán trong các trường hợp sau:
 Mỗi vật chỉ được chọn một lần.
 Mỗi vật được chọn nhiều lần (không hạn chế số lần)
Input: file văn bản Bag.inp
 Dịng 1: n, M cách nhau ít nhất một dấu cách
 n dòng tiếp theo: Mỗi dòng gồm 2 số Wi, Vi, là chi phí và giá trị đồ vật thứ i.
Output: file văn bản bag.out: Ghi giá trị lớn nhất tên trộm có thể lấy
Ví dụ

2. Hướng dẫn giải
 Trường hợp mỗi vật được chọn 1 lần: Vali B
Ta nhận thấy rằng: Giá trị của cái túi phụ thuộc vào 2 yếu tố: Có bao nhiêu vật đang được xét và trọng
lượng cịn lại cái túi có thể chứa được, do vậy chúng ta có 2 đại lượng biến thiên. Cho nên hàm mục
tiêu sẽ phụ thuộc vào hai đại lượng biến thiên. Do vậy bảng phương án của chúng ta sẽ là bảng 2 chiều.



Gọi 𝐹[𝑖][𝑗] là tổng giá trị lớn nhất của cái túi khi xét từ vật 1 đến vật i và trọng của cái túi chưa vượt
quá j. Với giới hạn 𝑗, việc chọn tối ưu trong số các vật {1,2, … , 𝑖 − 1, 𝑖} để có giá trị lớn nhất sẽ có hai
khả năng:
 Nếu khơng chọn vật thứ 𝑖 thì 𝐹[𝑖][𝑗] là giá trị lớn nhất có thể chọn trong số các vật
{1, 2, … , 𝑖 − 1} với giới hạn trọng lượng là j, tức là: 𝑭[𝒊][𝒋] = 𝑭[𝒊 − 𝟏][𝒋]


Nếu có chọn vật thứ 𝑖 (phải thỏa điều kiện 𝑊[𝑖] ≤ 𝑗) thì 𝐹[𝑖][𝑗] bằng giá trị vật thứ 𝑖 là 𝑉[𝑖]
cộng với giá trị lớn nhất có thể có được bằng cách chọn trong số các vật {1,2, … , 𝑖 − 1} với giới
hạn trọng lượng 𝑗 − 𝑊[𝑖] tức là về mặt giá trị thu được: 𝑭[𝒊][𝒋] = 𝑽[𝒊] + 𝑭[𝒊 − 𝟏][𝒋 − 𝑾[𝒊]]

Vậy chúng ta phải xem xét xem nếu chọn vật i hay khơng chọn vật i thì sẽ tốt hơn. Từ đó chúng ta có
cơng thức truy hồi như sau.
𝑭[𝟎][𝒋] = 𝟎 (hiển nhiên) – Bài toán con nhỏ nhất.
𝑭[𝒊][𝒋] = 𝒎𝒂𝒙(𝑭[𝒊 − 𝟏][𝒋], 𝑽[𝒊] + 𝑭[𝒊 − 𝟏][𝒋 − 𝑾[𝒊]]
 Trường hợp mỗi vật được chọn nhiều lần: Tương tự như suy luận ở trên ta xét: Vali A





Nếu khơng chọn vật thứ i thì F[i][j] là giá trị lớn nhất có thể chọn trong số các vật
{1,2, … , 𝑖 − 1} với giới hạn trọng lượng là j, tức là: 𝑭[𝒊][𝒋] = 𝑭[𝒊 − 𝟏][𝒋].



Nếu có chọn vật thứ i (phải thỏa điều kiện 𝑊[𝑖] ≤ 𝑗) thì 𝐹[𝑖][𝑗] bằng giá trị vật thứ 𝑖 là 𝑉[𝑖]
cộng với giá trị lớn nhất có thể có được bằng cách chọn trong số các vật {1,2, … , 𝑖} (vì vật 𝑖 vẫn
có thể được chọn tiếp) với giới hạn trọng lượng 𝑗 − 𝑊[𝑖] tức là về mặt giá trị thu được:


𝑭[𝒊][𝒋] = 𝑽[𝒊] + 𝑭[𝒊][ 𝒋 − 𝑾[𝒊]]
Do vậy chúng ta có cơng thức truy hồi như sau:
 𝑭[𝟎][𝒋] = 𝟎 (hiển nhiên) – Bài toán con nhỏ nhất.
 𝑭[𝒊][𝒋] = 𝒎𝒂𝒙(𝑭[𝒊 − 𝟏][𝒋], 𝑽[𝒊] + 𝑭[𝒊, 𝒋 − 𝑾[𝒊]]
3. Bảng phương án
Ví dụ (trường hợp mỗi vật chỉ được chọn 1 lần)
Bảng phương án:

Vậy chúng ta có thể chọn vật 2, 3, 4, 5.
Ví dụ (trường hợp mỗi vật được chọn nhiều lần)

Bảng phương án:


Chúng ta có thể chọn vật 4 (3 lần) và vật 5 (3 lần).
4. Truy vết
Trường hợp 1: Trong bảng phương án 𝐹[𝑛][𝑚] chính là giá trị lớn nhất thu được khi chọn trong cả n
vật với giới hạn trọng lượng là M.
Nếu 𝐹[𝑛]𝑀] = 𝐹[𝑛 − 1][𝑀] thì tức là không chọn vật thứ n, ta truy về 𝐹[𝑛 − 1][𝑀].
Cịn nếu 𝐹[𝑛][𝑀] ≠ 𝐹[𝑛 − 1][𝑀] thì ta thơng báo rằng phép chọn tối ưu có chọn vật thứ n và truy về
𝐹 𝑛 − 1, 𝑀 − 𝑊[𝑛] .
def Trace():
n1 = n
M1 = M
s= [] #List chua cac do vat da lay
while n1!= 0:
if F[n1][M1] != F[n1-1][M1]:
s.append(n1)
M1 -= V[n1-1]

n1 -= 1
s.reverse()
for i in s:
print(i,end=' ')
return 0

5. Cài đặt
Bài Vali B:
def KnapSack(M, W, V, n):
F = [[0 for x in range(M + 1)] for x in range(n + 1)]
for i in range(n + 1):
for j in range(M + 1):
if i == 0 or j == 0:
F[i][j] = 0
elif W[i-1] <= j:
F[i][j] = max(V[i-1] + F[i-1][j-W[i-1]], F[i-1][j])
else:
F[i][j] = F[i-1][j]
return F[n][M]

Bài vali A: cài tương tự
BÀI TẬP ÁP DỤNG
BÀI 9.3. 1. BÀI TOÁN CÁI TÚI
(câu 3, HSG Bạc Liêu, 2011-2012, bảng A, ngày 2)
Cho n đồ vật. Trọng lượng và giá trị của vật i lần lượt là A[i], B[i]. Hãy chọn ra một số vật sao cho tổng
trọng lượng của chúng không vượt quá trọng lượng M cho trước và tổng giá trị là lớn nhất. Cho biết 0 < n ≤ 20,
0 < M ≤ 100, 0 < A[i], B[i] < 256


Dữ liệu vào:

- Dòng đầu là 2 số n, M;
- Dòng i + 1 (1 ≤ i ≤ n) ghi 2 số nguyên dương A[i], B[i].
Dữ liệu ra:
- Mỗi dòng ghi 3 số: i, A[i], B[i];
- Dòng cuối là 3 số: Tổng số vật, tổng trọng lượng và tổng giá trị của các vật được chọn. Các số ghi trên
cùng một dịng được cách ít nhất một dấu cách.
Ví dụ
Input
Output
5 100
4 25 48
42 40
2 30 72
30 72
1 42 40
40 6
3 97 160
25 48
13 3
Code tham khảo:
def KnapSack(M, W, V, n):
for i in range(n + 1):
for j in range(M + 1):
if i == 0 or j == 0:
F[i][j] = 0
elif W[i-1] <= j:
F[i][j] = max(V[i-1] + F[i-1][j-W[i-1]], F[i-1][j])
else:
F[i][j] = F[i-1][j]
return 0

#Trace
def Trace():
n1 = n
M1 = M
s= [] #List chua cac do vat da lay
dem = 0
TL = 0
GT = 0
while n1!= 0:
if F[n1][M1] != F[n1-1][M1]:
print(n1,W[n1-1],V[n1-1])
#s.append(n1)
M1 -= W[n1-1]
dem += 1
TL += W[n1-1]
GT += V[n1-1]
n1 -= 1
print(dem, TL, GT)
return 0
# Driver code
fi = open('bag.inp','r')
fo = open('bag.out','w')
n, M = [int(x) for x in fi.readline().split()]
V = []
W = []
for i in range(n):
x, y = [int(x) for x in fi.readline().split()]
V.append(y)
W.append(x)



F = [[0 for x in range(M + 1)] for x in range(n + 1)]
KnapSack(M, W, V, n)
Trace()

BÀI 9.3. 2. TRỊ CHƠI (DẠNG BÀI TỐN CÁI TÚI - CHỌN NHIỀU VẬT)
Trong cuộc thi SV2012, các nhà tài trợ trao cho ban tổ chức N phần thưởng. Mỗi phần thưởng thuộc
loại thứ i có khối lượng là A[i] và giá trị C[i]. Số lượng các phần thưởng của mỗi loại không hạn chế. Đội chơi
nào chiến thắng trong cuộc thi sẽ được ban tổ chức tặng một cái túi dùng để đựng các phần thưởng và cái túi có
thể mang được tối đa khối lượng W. Hỏi các bạn trong đội chơi cần chọn những phần thưởng nào để cho tổng
giá trị của các phần thưởng đã chọn là lớn nhất nhưng khối lượng của chúng không vượt quá khối lượng W.
Biết mỗi loại phần thưởng có thể hoặc khơng chọn phần thưởng nào, hoặc chọn một phần thưởng, hoặc chọn
nhiều phần thưởng.
Dữ liệu vào: Từ tệp văn bản BAG.INP gồm:
● Dòng 1: Ghi hai số nguyên N và W (1 ≤ N, W ≤ 1000).
● N dòng tiếp theo, dòng thứ i ghi hai số nguyên A[i] và C[i] (1 ≤ A[i], C[i] ≤ 1000).
Dữ liệu ra: Ghi ra tệp văn bản BAG.OUT gồm:
● Dòng 1: Ghi tổng giá trị phần thưởng lớn nhất được chọn.
● Các dòng tiếp theo ghi chỉ số và số lượng của các loại phần thưởng đã chọn.
Ví dụ:
BAG.INP
BAG.OUT
Giải thích
5 13
19
Chọn 1 phần thưởng loại 1, chọn 5 phần thưởng loại 4. Tổng
34
11
giá trị phần thưởng lớn nhất được chọn là:
45

45
1x4 + 3x5 = 19.
56
23
11

Hướng dẫn: Áp dụng cách giải bài toán ValiA
Địa chỉ giải bài trực tuyến: />


×