You are on page 1of 20

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ó 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 toán tổng quát 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 toán con và đi giải từng
bài toá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 toá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 toá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 toán lớn nhất (bài
toá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 Cách 2
program Fibo1; program Fibo2;
var
function F(i: Integer): Integer;
F: array[1..6] of Integer;
begin i: Integer;
if i < 3 then F := 1
begin
else F := F(i - 1) + F(i - 2);
end; F[1] := 1; F[2] := 1;
for i := 3 to 6 do
begin
F[i] := F[i - 1] + F[i - 2];
WriteLn(F(6)); WriteLn(F[6]);
end. 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 … Quá trình tính toá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).

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 toá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
toán con đó cho ta lời giải của bài toán lớn.
Vì quy hoạch động là đi giải tất cả các bài toá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.
Quá trình từ bài toán cơ sở tìm ra lời giải bà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 là bài toá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 toán lớn gọi là công
thức truy hồi (hay phương trình truy toán) của quy hoạch động
- Tập các bài toán nhỏ nhất có ngay lời giải để từ đó giải quyết các bài toá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 toá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 toá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 toá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ản 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.
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 toá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 toá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 toá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 toán con dưới một hình thức nào đó để phối hợp tìm
được nghiệm bài toán lớn"
MỘT SỐ BÀI TOÁN QHĐ CƠ BẢN
Bài toán 1: Dãy con đơn điệu tăng dài nhất:
Cho dãy A gồm n số nguyên, ký hiệu [a0, a1,..., an-1] . Tìm một dãy con đơn điệu tăng dài
nhất của dãy A, biết một dãy con của A là dãy có được từ A bằng cách xóa đi một số phần tử
của A.
Ví dụ: dãy [1, 5, 9, 2, 3, 11, 8, 10, 4] có dãy con đơn điệu tăng dài nhất là [1, 2, 3, 8, 10].
Input: đọc từ tệp Day.inp gồm các số nguyên trong dãy A được viết cách nhau bởi dấu cách
Output: Ghi kết quả tính được vào tệp Day.out gồm 2 dòng
- Dòng 1: ghi độ dài của dãy con dài nhất
- Dòng 2: Ghi lần lượt từng phần tử của dãy con.
Cách giải:
Bổ sung vào a 2 phần tử a[0]=-∞ và a[n+1]=+∞ khi đó dãy đơn điệu 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 tính L[i] bằng độ dại dãy con đơn điệu tăng dài nhất bắt đầu tại a[i].
* cơ sở QHĐ
L[n+1]=1 vì dãy này chỉ gồm 1 phần tử là +∞
* 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]; L[i+2], ….L[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 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]
* 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 …
Quá 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
<Thông báo chọn a[i]>
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à:

i 0 1 2 3 4 5 6 7 8 9 10 11
ai − ∞ 5 2 3 4 9 10 5 6 7 8+∞
L[i] 9 5 8 7 6 3 2 5 4 3 2 1
T[i] 2 8 3 4 7 6 11 8 9 10 11

Cách 1: Duyệt từ cuối dãy về đầ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) do
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 2 phần tử canh 2 đầu dãy}
L[n+1]:= 1; {Khởi tạo cơ sở QHĐ}
for i:= n downto 0 do {tính bảng phương án}
begin
{chọn trong các chỉ số j đứng sau i thỏa mãn a[j]>a[i] chọn ra jmax có L[jmax] lớn nhất}
jmax:=n+1;
for j:=i+1 to n+1 do
if (a[i]<a[j]) and (L[j]>L[jmax]) then jmax:=j;
L[i] := L[jmax]+1; {lưu độ dài dãy tăng dài nhất tại ai}
T[i] := jmax; {lưu vết: phần tử đứng liền sau a[i] trong dãy con tăng dài nhất đó là
a[jmax]}
end;
Writeln(fo,L[0]-2); { độ dài dãy con tăng dài nhất =L[0]-2 }
i:=T[0]; {bắt đầu truy vết tìm nghiệm}
while i<>n+1 do
begin
write(fo,a[i],' '); i:=T[i];
end;
close(fo);
END.
Cách 2: Duyệt từ đầu dãy về cuối 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) do
begin
N:=N+1; read(fi,a[N]);
end;
close(fi);
end;
Procedure quyhoachdong;
Begin
L[0]:=1;a[0]:=low(integer);a[N+1]:=high(integer);
for i:=1 to N+1 do
begin
jmax:=0;
for j:=0 to i-1 do
if (a[j]<a[i]) and (L[j]>L[jmax]) then jmax:=j;
L[i]:=L[jmax]+1;
T[i]:=jmax;
end;
End;
Procedure Truyvet(i:integer); {dùng đệ quy để in dãy tối ưu}
Begin
i:=T[i];
if i=0 then exit;
truyvet(i);
write(fo,a[i],' ');
end;
Procedure ghidulieu;
Begin
assign(fo,'day.out'); rewrite(fo);
quyhoachdong;
writeln(fo,'so luong phan tu=',l[n+1]-2);
truyvet(N+1);
close(fo);
End;
BEGIN
Nhapdulieu;
Ghidulieu;
END.
HÌNH VUÔNG 0 1 – QBSQUARE – SPOJ
Đăng trong 17/05/2014 bởi connhangheovodanh
 ĐỀ BÀI :
QBSQUARE – SPOJ
Cho một bảng kích thước MxN, được chia thành lưới ô vuông đơn vị M dòng N cột ( 1 <= M, N
<= 1000 )
Trên các ô của bảng ghi số 0 hoặc 1. Các dòng của bảng được đánh số 1, 2… M theo thứ tự từ
trên xuống dưới và các cột của bảng được đánh số 1, 2…, N theo thứ tự từ trái qua phải
Yêu cầu:
Hãy tìm một hình vuông gồm các ô của bảng thoả mãn các điều kiện sau:
1 – Hình vuông là đồng nhất: tức là các ô thuộc hình vuông đó phải ghi các số giống nhau (0
hoặc 1)
2 – Cạnh hình vuông song song với cạnh bảng.
3 – Kích thước hình vuông là lớn nhất có thể
Input
Dòng 1: Ghi hai số m, n
M dòng tiếp theo, dòng thứ i ghi N số mà số thứ j là số ghi trên ô (i, j) của bảng
Output
Gồm 1 dòng duy nhất ghi kích thước cạnh của hình vuông tìm được
Example
Input:
11 13
0000010000000
0000111000000
0011111110000
0011111110000
0111111111000
1111111111100
0111111111000
0011111110000
0011111110000
0000111000011
0000010000011

Output:
7
 THUẬT TOÁN :
Đây là một bài quy hoạch động hay
Gọi f[i][j] là kích thước của hình vuông có góc đáy là ô [i][j].
Vậy thì nếu bốn ô : bên trái, bên phải, chéo trên đều cùng giá trị với ô này thì nó sẽ tạo được một
ô vuông, nếu không thì xem như ô đó có kích thước chỉ là 1 vì chỉ tự nó tạo được một ô vuông
Bây giờ ta xét đên công thức QHĐ đối với ô đang xét ở trên. Đương nhiên ta chỉ có thể lấy giá
trị nhỏ nhất trong các f của các ô ở trên để chắc chắn tạo thành ô vuông
f[i,j] := min(f[i-1,j],f[i,j-1],f[i-1,j-1]) + 1
Cơ sở QHĐ của bài toán chính là các ô cạnh trên và bên trái có f = 1 vì các ô có góc dưới của nó
chính là chính nó và kích thước là 1
Đây là code mình đã submit và ac
 CODE :
uses math;
const fi = '';
fo = '';
var m,n,i,j,maxf : integer;
a : array[0..1001,0..1001] of 0..1;
f : array[0..1001,0..1001] of integer;

begin
assign(input,fi);
reset(input);
assign(output,fo);
rewrite(output);

readln(m,n);
for i := 1 to m do
for j := 1 to n do
read(a[i,j]);
for j := 0 to n+1 do f[1,j] := 1;
for i := 0 to m+1 do f[i,1] := 1;
for i := 1 to m do
for j := 1 to n do
if (a[i,j] = a[i-1,j]) and
(a[i,j] = a[i,j-1]) and
(a[i,j] = a[i-1,j-1]) then
f[i,j] := min(f[i-1,j],min(f[i,j-1],
f[i-1,j-1])) + 1
else
f[i,j] := 1;
maxf := 1;
for i := 1 to m do
for j := 1 to n do
maxf := max(maxf,f[i,j]);
writeln(maxf);

close(input);
close(output);
end.
BÀI TOÁN CÁI TÚI

Trong siêu thị có n gói hàng (n <= 100), gói hàng thứ i có trọng lượng là Wi <= 100 và trị giá Vi
<= 100. Một tên trộm đột nhập vào siêu thị, sức của tên trộm không thể mang được trọng
lượng vượt quá M ( M <= 100). Hỏi tên trộm sẽ lấy đi những gói hàng nào để được tổng giá
trị lớn nhất.
Cách giải:
Nếu gọi B[i, j] là giá trị lớn nhất có thể có bằng cách chọn trong các gói {1, 2, ..., i} với giới
hạn trọng lượng j. Thì giá trị lớn nhất khi được chọn trong số n gói với giới hạn trọng lượng
M chính là B[n, M].
1. Công thức truy hồi tính B[i, j].
Với giới hạn trọng lượng j, việc chọn tối ưu trong số các gói {1, 2, ...,i - 1, i} để có giá trị lớn
nhất sẽ có hai khả năng:
• Nếu không chọn gói thứ i thì B[i, j] là giá trị lớn nhất có thể bằng cách chọn trong số các gói
{1, 2, ..., i - 1} với giới hạn trọng lượng là j. Tức là
B[i, j] = B[i - 1, j]
• Nếu có chọn gói thứ i (tất nhiên chỉ xét tới trường hợp này khi mà Wi  j) thì B[i, j] bằng giá
trị gói thứ i là Vi cộng với giá trị lớn nhất có thể có được bằng cách chọn trong số các gói {1,
2, ..., i - 1} với giới hạn trọng lượng j - Wi. Tức là về mặt giá trị thu được:
B[i, j] = Vi + B[i - 1, j - Wi]
Vì theo cách xây dựng B[i, j] là giá trị lớn nhất có thể nên nó sẽ là max trong 2 giá trị thu
được ở trên.
2. Cơ sở quy hoạch động:
Dễ thấy B[0, j] = giá trị lớn nhất có thể bằng cách chọn trong số 0 gói = 0.
3. Tính bảng phương án:
Bảng phương án B gồm n + 1 dòng, M + 1 cột, trước tiên được điền cơ sở quy hoạch động:
Dòng 0 gồm toàn số 0. Sử dụng công thức truy hồi, dùng dòng 0 tính dòng 1, dùng dòng 1
tính dòng 2, v.v... đến khi tính hết dòng n.

0 1 ... M
0
0000
1
2
... ...
n
4. Truy vết:
Tính xong bảng phương án thì ta quan tâm đến b[n, M] đó chính là giá trị lớn nhất thu được
khi chọn trong cả n gói với giới hạn trọng lượng M. Nếu b[n, M] = b[n - 1, M] thì tức là không
chọn gói thứ n, ta truy tiếp b[n - 1, M]. Còn nếu b[n, M]  b[n - 1, M] thì ta thông báo rằng
phép chọn tối ưu có chọn gói thứ n và truy tiếp b[n - 1, M - Wn]. Cứ tiếp tục cho tới khi truy
lên tới hàng 0 của bảng phương án.
program The_Bag;
const
max = 100;
var
W, V: Array[1..max] of Integer;
B: array[0..max, 0..max] of Integer;
n, M: Integer;

procedure Enter; {Nhập dữ liệu}


var
i: Integer;
begin
Write('n = '); Readln(n);
for i := 1 to n do
begin
Writeln('Pack ', i);
Write(' + Weight : '); Readln(W[i]);
Write(' + Value : '); Readln(V[i]);
end;
Write('M = '); Readln(M);
end;

procedure Optimize; {Tính bảng phương án bằng công thức truy hồi}
var
i, j: Integer;
begin
FillChar(B[0], SizeOf(B[0]), 0); {Điền cơ sở quy hoạch động}
for i := 1 to n do
for j := 0 to M do
begin
B[i, j] := B[i - 1, j]; {Giả sử không chọn gói thứ i thì B[i, j] = B[i - 1,
j]}
{Sau đó đánh giá: nếu chọn gói thứ i sẽ được lợi hơn thì đặt lại B[i, j]}
if (j >= W[i]) and (B[i, j] < B[i - 1, j - W[i]] + V[i]) then
B[i, j] := B[i - 1, j - W[i]] + V[i];
end;
end;

procedure Trace; {Truy vết tìm nghiệm tối ưu}


begin
Writeln('Max Value : ', B[n, M]); {In ra giá trị lớn nhất có thể kiếm được}
Writeln('Selected Packs: ');
while n <> 0 do {Truy vết trên bảng phương án từ hàng n lên hàng 0}
begin
if B[n, M] <> B[n - 1, M] then {Nếu có chọn gói thứ n}
begin
Writeln('Pack ', n, ' W = ', W[n], ' Value = ', V[n]);
M := M - W[n]; {Đã chọn gói thứ n rồi thì chỉ có thể mang thêm được trọng
lượng M - Wn nữa thôi}
end;
Dec(n);
end;
end;

begin
Enter;
Optimize;
Trace;
end.

Bài toán tìm tổng lớn nhất của 1 ma trận: Cho 1 lưới ô vuông kích thước NxN ( 0<N<=100).
TÌm đường đi từ ô (1,1) đến ô (N,N) sao cho tổng các chữ số trên đường đi là lớn nhất.Biết rằng 1
lần di chuyển chỉ đi sang phải 1 ô hoặc đi xuống dưới 1 ô.Tìm tổng lớn nhất và ghi lại đường đi đó
thành từng dòn,1 dòng là 1 cặp số chỉ toạ độ x,y của các ô từ vị trí xuất phát(1,1) đến ô (n,n).
vd:
input
5 {la so N}
27265
71814
49364
11952
95261
output
46 {tong}
1 1 {cac dong sau la toa do cac vi tri tuong ung}
21
31
32
33
4 3
4 4
5 4
5 5
Code: onst
spt = 101;
fi = 'f.i';
fo = 'f.o';
var
n: byte;
a, b: array[0..spt,0..spt] of integer;
c: array[0..spt,0..spt] of 0..1;

procedure Input;
var
f: text;
i, j: byte;
begin
assign(f, fi);
reset(f);
readln(f,n);
for i := 1 to n do
begin
for j := 1 to n do
read(f,a[i,j]);
readln(f);
end;
close(f);
end;

procedure Process;
var
i,j: byte;
begin
for i := 1 to n do
for j := 1 to n do
begin
b[i,j] := 0;
c[i,j] := 0;
end;
b[1,1] := a[1,1];
for i := 2 to n do
begin
b[1,i] := b[1,i-1] + a[1,i];
b[i,1] := b[i-1,1] + a[i,1];
end;
for i := 2 to n do
for j := 2 to n do
if b[i-1,j] > b[i,j-1] then b[i,j] := b[i-1,j] + a[i,j]
else b[i,j] := b[i,j-1] + a[i,j];
c[n,n] := 1;
for i := n downto 1 do
for j := n downto 1 do
if c[i,j] = 1 then
if b[i-1,j] > b[i,j-1] then
c[i-1,j] := 1
else c[i,j-1] := 1;
end;

procedure Result;
var
f: text;
i, j: byte;
begin
assign(f, fo);
rewrite(f);
writeln(f, b[n,n]);
for i := 1 to n do
for j := 1 to n do
if c[i, j] = 1 then
writeln(f, i, ' ', j);
close(f);
end;
begin
Input;
Process;
Result;
end.

Đề Bình Định năm 2015


Bài 3: Tìm đường hái quả (6,0 điểm):
Một khu vườn hình chữ nhật kích thước MxN được chia thành các ô vuông đơn vị để trồng
một loại cây ăn quả. Trên mỗi ô thì số quả tương ứng có thể hái được là A[i,j] (1iM; 1jN).
Một người khách dạo qua vườn và hái tất cả các quả trên những ô đi qua. Vị trí xuất phát từ ô
[1,1] và kết thúc tại ô [M,N] với hành trình là sang ô chung cạnh theo hướng tăng của i hoặc j
(sang phải hoặc đi xuống – như hình vẽ). Hãy viết chương trình tìm lộ trình đi của người đó để
hái được nhiều quả nhất.

1 3 5 7 2
7 9 4 2 2
2 3 1 6 7
7 4 6 2 5

Dữ liệu vào là tệp HAIQUA.INP có cấu trúc như sau:
- Dòng đầu tiên là hai số M, N nguyên dương cách nhau một khoảng cách (0<M,N<100).
- M dòng tiếp theo, mỗi dòng gồm N số tương ứng là số quả có thể hái được ở các ô theo thứ tự
tại hàng thứ i. Mỗi số cách nhau một khoảng cách.
Dữ liệu ra là tệp HAIQUA.OUT cso cấu trúc như sau:
- Dòng đầu là số quả lớn nhất có thể hái được theo một lộ trình thỏa mãn yêu cầu.
- Dòng tiếp theo gồm M+N-1 số tương ứng là số quả hái ở từng ô theo đường đi để được số quả
nhiều nhất. Mỗi số cách nhau ít nhất một khoảng cách.
Ví dụ:
HAIQUA.INP HAIQUA.OUT
45 41
13572 17942675
79422
23167
74625
Uses Crt;
Const fi='D:\Haiqua.Inp';
fo='D:\Haiqua.Out';
Type m2c=Array[1..100,1..100] of Word;
mmc=Array[1..100] of Word;
Var i,j,k,m,n,max:Byte;
a:m2c;
b:mmc;
f,g:Text;
Begin
Assign(f,fi);
Reset(f);
Readln(f,m,n);
For i:=1 to m do
Begin
For j:=1 to n do
Read(f,a[i,j]);
Readln(f);
End;
Close(f);
max:=a[1,1];
k:=1; i:=1; j:=1;
fillchar(b,sizeof(b),0);
b[1]:=max;
While (i<=m) and (j<=n) do
Begin
inc(k);
If a[i+1,j]>a[i,j+1] then
Begin
inc(i);
max:=max+a[i,j];
b[k]:=a[i,j];
End
Else
Begin
If a[i+2,j]>a[i,j+2] then
Begin
inc(i);
max:=max+a[i,j];
b[k]:=a[i,j];
End
Else
inc(j);
max:=max+a[i,j];
b[k]:=a[i,j];
End;
End;
Dec(k);
Assign(g,fo);
Rewrite(g);
Writeln(g,max);
For i:=1 to k do
Write(g,b[i],' ');
Close(g);
End.
13

Con kiến
Published: 20 August 2017 | Hits: 1892

Đề bài: Có một ống hình trụ, khi trải phẳng ra có thể là một bảng MxN ô. Giá trị A[i,j] là lượng thức ăn có ở ô ở dòng i cột
j. Một con kiến xuất phát từ một ô ở mép bên trái của hình trụ và b sang mép bên phải. Từ ô (i,j) kiến có thể bò sang 1
trong 3 ô (i–1,j+1), (i,j+1) hoặc (i+1,j+1). (Chú ý: vì ống hình trụ nên kiến đang ở dòng 1 có thể bò xuống dòng M và
ngược lại). Bò qua ô nào thì kiến mang theo toàn bộ lượng thức ăn ở ô đó. Hãy tìm đường đi mà kiến kiếm được nhiều
thức ăn nhất.
const fi = 'CONKIEN.INP';
fo = 'CONKIEN.OUT';
MAXM = 1000;
MAXN = 1000;

var g : text;
N, M : integer;
a: array [1..MAXM, 1..MAXN] of integer;
F: array [1..MAXM, 1.. MAXN] of integer;

procedure Nhap;
var i, j : integer;
begin
assign(g, fi); reset(g);
readln(g, M, N);
for i:=1 to M do
begin
for j := 1 to N do read(g, a[i,j]);
//readln(g);
end;
end;

function Max3(x, y, z : integer) : integer;


var max : integer;
begin
max := x;
if max < y then max := y;
if max < z then max := z;
exit(max);
end;

function TT1(i: integer): integer;


begin
if i = 0 then exit(M)
else exit(i-1);
end;

function TT2(i: integer): integer;


begin
if i = M then exit(1)
else exit(i+1);
end;

procedure QHD;
var i, j: integer;
begin
for i:=1 to M do F[i, 1] := a[i, 1];
14

for j:= 2 to N do
for i:= 1 to M do
F[i, j] := Max3(F[TT1(i), j-1], F[i,j-1], F[TT2(i), j-1])+ a[i, j];
end;

procedure TruyVet(i, j : integer);


begin
if j = 1 then
write(g, '(', i, ', ' , j, ') ')

else // i > 1
begin
if F[i, j] = F[i-1, j-1] + a[i, j] then
TruyVet(i-1, j-1)
else
if F[i, j] = F[i, j-1] + a[i, j] then
TruyVet(i, j-1)
else TruyVet(i+1, j-1);
write(g, ' -> (', i, ', ', j, ')');
end;
end;

procedure InKQ;
var max, i, k: integer;
begin
assign(g, fo); rewrite(g);
max:= -1;
for i:= 1 to M do
if max < F[i, N] then
begin
max:= F[i, N];
k:= i;
end;
writeln(g, max);
TruyVet(k, N);
close(g);
end;

BEGIN
Nhap;
QHD;
InKQ;

END.
15
16

Bài toán 2: Mê cung

Trong một chuyến thám hiểm mạo hiểm, một đoàn thám hiểm không may lọt vào một mê cung với nhiều
cạm bẫy. Trong mê cung đó chỉ có một lối ra duy nhất, lối ra bao gồm các ô hình vuông được xếp
thành một hàng dài. Muốn đi được ra ngoài mọi người phải bước qua một hàng các ô hình vuông đó
và phải bước theo quy tắc sau:
- Quy tắc 1: Mỗi bước chỉ có thể bước một ô, hai ô hoặc ba ô
- Quy tắc 2: Từ người thứ 2 trở đi bước theo quy tắc 1 và không được trùng với các cách bước của tất
cả những người đi trước đó.
Hỏi đoàn thám hiểm đó còn lại tối thiểu bao nhiêu người không thể thoát ra khỏi mê cung đó.
Dữ liệu vào: mecung.inp
- Dòng 1: ghi số nguyên m (m<=1018) là số người trong đoàn thám hiểm
- Dòng 2: ghi một số nguyên n (n<=70) là tổng số ô vuông
Dữ liệu ra: mecung.out
Gồm một số nguyên duy nhất thể hiện số người còn lại tối thiểu bao nhiêu người không thể thoát ra
khỏi mê cung
Ví dụ:
mecung.inp mecung.out
20 7
5

Bài toán 3: Dãy con chung dài nhất

Cho 2 dãy số nguyên A=<a1,a2,..,an> và B=<b1,b2,…,bm>. Hãy tìm dãy số nguyên C sao cho C là dãy
con chung dài nhất của A và B.
Dữ liệu vào: từ tệp daychung.inp gồm 3 dòng
- Dòng 1 ghi 2 số nguyên n,m là độ dài 2 dãy
- Dòng 2 ghi n số nguyên cách nhau bởi dấu cách
- Dòng 3 ghi m số nguyên cách nhau bởi dấu cách
Dữ liệu ra: Ghi vào tệp daychung.out gồm 2 dòng
- Dòng 1 ghi 1 số nguyên thể hiện độ dài xâu cong chung dài nhất
- Dòng 2 ghi các phần tử của xâu con chung, mỗi phần tử cách nhau bởi dấu cách.
v Ví dụ:

daychung.inp daychung.out
66 4
157896 1796
123796

Bài toán 4: Xâu con chung dài nhất (như bài trên)

Xâu kí tự A được gọi là xâu con của xâu kí tự B nếu ta có thể xóa đi một số kí tự trong xâu B để được
xâu A.
Cho 2 xâu ký tự X,Y. Hãy tìm xâu kí tự Z có độ dài lớn nhất và là con của cả X và Y.
Input:
17

Dòng 1 chứa xâu X


Dòng 2 chứa xâu Y
Output: chỉ gồm 1 dòng ghi độ dài xâu Z tìm được
Ví dụ:
Input.inp Output.out
ALGORITHM 7
LOGARITHM

Bài toán 5: Ba lô (cái túi)

Cho n gói hàng (n<=1000). Gói hàng thứ i có khối lượng là W[i]<=1000 và giá trị V[i]<=1000. Cần
chọn những gói hàng nào để bỏ vào một ba lô sao cho tổng giá trị của các gói hàng đã chọn là lớn nhất
nhưng tổng khối lượng của chúng không vượt quá khối lượng M<=1000 cho trước. Mỗi gói chỉ chọn 1
hoặc không chọn.
Input data: cho file văn bản balo.inp
- - Dòng 1: N, M cách nhau ít nhất 1 dấu cách
- - N dòng tiếp theo: mỗi dòng gồm 2 số Wi và Vi là khối lượng và giá trị gói hàng
Output data: ghi kết quả vào file văn bản balo.out giá trị lớn nhất của các gói hàng mà balo có thể
chứa.

Ví dụ:
balo.inp balo.out
5 13 16
3 4
4 5
5 6
2 3
1 1
Giải thích
n = 5; M = 13
I 1 2 3 4 5
W[i] 3 4 5 2 1
V[i] 4 5 6 3 1

Tổng giá trị của các gói hàng bỏ vào ba lô: 16

Các gói được chọn: 1(3, 4) 2(4, 5) 3(5, 6) 5(1, 1)


Cách giải:
Gọi f[i,j] là tổng giá trị lớn nhất của túi khi xét từ vật 1 đến vật i với trọng lượng không vượt quá j
(trọng lượng giới hạn).
Khi xét vật i thì có 2 khả năng
- Khả năng 1: nếu trọng lượng của vật i là w[i] lớn hơn trọng lượng giới hạn j (w[i]>j) thì giá trị
của vật i bằng giá trị của vật i-1 tại giới hạn j.
Tức là f[i,j]=f[i-1,j]
18

- Khả năng 2: nếu trọng lượng vật i là w[i] nhỏ hơn hoặc bằng giới hạn j (w[i]<=j) thì có thể bỏ
được vật i vào túi, trọng lượng còn lại của túi là j-w[i] có thể chọn từ các vật từ 1 đến i-1. {giải
thích: túi chỉ chứa được trọng lượng j, khi đã bỏ vào túi trọng lượng w[i] thì trọng lượng còn lại của
túi là j-w[i] được chọn từ các vật trước đó}. Vậy giá trị của túi khi bỏ vật i vào là giá trị của vật i-1 tại
giới hạn j-w[i] cộng với giá trị của vật i là v[i].
Tức là f[i,j]=f[i-1,j-w[i]]+v[i]
Với 2 khả năng có thể xét ở trên thì ta lấy giá trị của khả năng nào tối ưu nhất ?
Muốn biết khả năng nào tối ưu thì ta xét xem giá trị của khả năng nào lớn hơn thì ta lấy khả năng đó.
Tức là F[i,j]=max(f[i-1,j], f[i-1,j-w[i]]+v[i])
code QHĐ

f[0,j]:=0;
for i:=1 to n do
for j:=1 to m do
if w[i]<=j then
f[i,j]:=max(f[i-1,j],f[i-1,j-w[i]]+v[i])
else f[i,j]:=f[i-1,j];
write(fo,f[n,m]);
Bài toán 6: Farmer (người nông dân)

Một người có N mảnh đất và M dải đất. Các mảnh đất có thể coi là một tứ giác và các dải đất thì coi
như một đường thẳng. Dọc theo các dải đất ông ta trồng các cây bách, dải đất thứ i có Ai cây bách. Ông
ta cũng trồng các cây bách trên viền của các mảnh đất, mảnh đất thứ j có Bj cây bách. Cả ở trên các
mảnh đất và dải đất, xen giữa 2 cây bách ông ta trồng một cây ôliu. Ông ta cho con trai được chọn các
mảnh đất và dải đất tuỳ ý với điều kiện tổng số cây bách không vượt quá Q. Người con trai phải chọn
thế nào để có nhiều cây ôliu (loài cây mà anh ta thích) nhất.

Input data: dữ liệu được cho trong file Famer.inp


Dòng đầu tiên bao gồm: Đầu tiên là số Q (0<=Q<=150000) là số cây bách mà người con được chọn;
sau đó là số nguyên M là số cánh đồng; tiếp theo là số nguyên K là số dải đất.
- Dòng thứ hai chứa M số nguyên N1, N2, …, Nm (3<= N1, N2, …, Nm<=150) là số cây bách trên cánh
đồng
- Dòng thứ 3 chứa K số nguyên R1, R2, …, Rk (2<= R1, R2, …, Rk <= 150) là số cây bách trên dải đất.
Output data: Ghi ra tệp Farmer.out gồm 1 số nguyên duy nhất thể hiện số cây oliu lớn nhất mà người
con có thể thừa hưởng.
Ví dụ
Famer.inp Famer.out
17 3 3 17
13 4 8
4 8 6

Bài toán 7: Đổi tiền


19

Bạn được cho một tập hợp các mệnh giá tiền. Tập hợp luôn chứa phần tử mang giá trị 1. Mỗi mệnh giá
có vô hạn các đồng tiền mang mệnh giá đó. Cho số tiền S, hãy tìm cách đổi S thành ít đồng tiền nhất,
sao cho mỗi đồng tiền có mệnh giá thuộc tập hợp đã cho.
Input data: Dữ liệu vào được đọc từ tệp doitien.inp gồm 2 dòng:
- Dòng 1: Hai số nguyên dương N (số phần tử của tập hợp mệnh giá tiền) và S (số tiền cần đổi) (1 ≤
N ≤ 100; 1 ≤ S ≤ 106 ).
- Dòng 2: N số nguyên dương biểu thị mệnh giá của các phần tử trong tập hợp (giá trị không vượt
quá 100).
Output data: Ghi kết quả ra tệp doitien.out gồm một số nguyên duy nhất là số đồng tiền ít nhất có thể
đổi được.

Ví dụ
doitien.inp doitien.out
2 3 2
1 2

Bài toán 8: Bố trí phòng họp

Có n cuộc họp, cuộc họp thứ i bắt đầu vào thời điểm ai và kết thúc ở thời điểm bi. Do chỉ có một phòng
hội thảo nên 2 cuộc họp bất kì sẽ được cùng bố trí phục vụ nếu khoảng thời gian làm việc của chúng
không giao nhau hoặc chỉ giao nhau tại đầu mút. Hãy bố trí phòng họp để phục vụ được nhiều cuộc họp
nhất.

Input data: file văn bản phonghop.inp


- Dòng 1: số nguyên dương N (N<=1000)
- N dòng tiếp theo: dòng thứ i chứa 2 số nguyên dương (ai,bi<=1000) chỉ thời điểm bắt đầu và thời
điểm kết thúc của cuộc họp i
Output data: file văn bản phonghop.out. Một số nguyên duy nhất thể hiện số cuộc họp nhiều nhất
Ví dụ:

phonghop.inp phonghop.out
4 3
45
56
16
69

Bài toán 9: Cho thuê máy

Trung tâm tính toán hiệu năng cao nhận được đơn đặt hàng của N khách hàng. Khách hàng i muốn
sử dụng máy trong khoảng thời gian từ ai đến bi và trả tiền thuê là ci. Hãy bố trí lịch thuê máy để tổng
số tiền thu được là lớn nhất mà thời gian sử dụng máy của 2 khách hàng bất kì được phục vụ đều không
giao nhau (cả trung tâm chỉ có một máy cho thuê)
Input data: file văn bản thuemay.inp
- Dòng 1: số nguyên dương N (N<=10000)
20

- N dòng tiếp theo: dòng thứ i chứa 3 số nguyên dương (ai,bi,ci<=100


Output data: file văn bản thuemay.out. Một số nguyên duy nhất thể hiện số tiền lớn nhất thu được.
Ví dụ:

thuemay.inp thuemay.out
3 16
1 8 16
276
799

Bài toán 10: Tiền khách sạn


Trong dịp nghi lễ 30 tháng 4 và 1 tháng 5 vừa qua do cùng đợt nghỉ với ngày giỗ tổ Hùng Vương 10
tháng 3(âm lịch) nên số ngày nghỉ lễ tăng lên. Vì thế lượng khách du lịch đổ về Nha Trang tham quan
cũng tăng kỷ lục, dẫn đến tinh trạng các khách sạn ở đây “cháy phòng”. Khách sạn Quang Huy chỉ còn
một phòng nên quyết định cho thuê phòng này theo hình thức thỏa thuận về giá cả. Sau khi tổng hợp
các đơn đặt hàng, khách sạn nhận được n đơn đặt hàng, trong đó đơn đặt hàng thứ i đăng ký ngày bắt
đầu là ai, ngày trả phòng là bi và chấp nhận trả số tiền thuê phòng là ci.
Do có nhiều đơn đặt hàng, thời gian đặt phòng lại chồng chéo nhau, số tiền khách hàng chấp nhận
trả cho khách sạn cũng khác nhau nên ban quản lý khách sạn đang rất khó khăn không biết nhận lời hay
từ chối khách hàng nào.
Yêu cầu: Viết chương trình giúp khách sạn nhận đơn đặt phòng sao cho lợi nhuận thu được là lớn
nhất.
Lưu ý: Theo điều lệ của khách sạn, khách hàng phải trả phòng trước 12 giờ trưa, khách hàng khác
có thể nhận phòng từ 12 giờ trong một ngày.
Dữ liệu vào: được ghi trên tệp tienks.inp bao gồm:
o Dòng thứ nhất là số nguyên n (1 ≤ n ≤ 12000) thể hiện số đơn đặt hàng.
o n dòng tiếp theo gồm 3 số nguyên ai, bi và ci. Mỗi số cách nhau một khoảng trắng với ràng buộc(l ≤ai
≤ bi ≤ 100, 0 ≤ c ≤1000).
Dữ liệu ra: lưu trong tệp tienks.out với một số nguyên thể hiện số tiền lớn nhất

TIENKS.INP TIENKS.OUT
3 20
128
Vi dụ 1
236
476
4 17
145
Vi dụ 2 138
354
469

You might also like