Tải bản đầy đủ (.doc) (22 trang)

QUY HOẠCH ĐỘNG trường THPT chuyên biên hòa – hà nam

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 (262.25 KB, 22 trang )

QUY HOẠCH ĐỘNG
Trường THPT Chuyên Biên Hòa – Hà Nam

1. Bài toán quy hoạch động
Trong toán học và khoa học máy tính, quy hoạch động (dynamic
programming) là một phương pháp hiệu quả giải những bài toán tối ưu có ba
tính chất sau đây:
 Bài toán lớn có thể phân rã thành những bài toán con đồng dạng,
những bài toán con đó có thể phân rã thành những bài toán nhỏ hơn nữa …
(recursive form).
 Lời giải tối ưu của các bài toán con có thể sử dụng để tìm ra lời giải tối
ưu của bài toán lớn (optimal substructure).
 Hai bài toán con trong quá trình phân rã có thể có chung một số bài
toán con khác (overlapping subproblems).
Tính chất thứ nhất và thứ hai là điều kiện cần của một bài toán quy
hoạch động. Tính chất thứ ba nêu lên đặc điểm của một bài toán mà cách giải
bằng phương pháp quy hoạch động hiệu quả hơn hẳn so với phương pháp giải
đệ quy thông thường.
Với những bài toán có hai tính chất đầu tiên, chúng ta thường nghĩ đến
các thuật toán chia để trị và đệ quy: Để giải quyết một bài toán lớn, ta chia nó
ra thành nhiều bài toán con đồng dạng và giải quyết độc lập các bài toán con
đó.
Khác với thuật toán đệ quy, phương pháp quy hoạch động thêm vào cơ
chế lưu trữ nghiệm hay một phần nghiệm của mỗi bài toán khi giải xong
nhằm mục đích sử dụng lại, hạn chế những thao tác thừa trong quá trình tính
toán.
2. Cách giải một bài toán quy hoạch động
Việc giải một bài toán quy hoạch động phải qua khá nhiều bước, ta có
thể hình dung:
1



Quy hoạch động = Chia để trị + Cơ chế lưu trữ nghiệm để sử dụng lại
(Dynamic Programming = Divide and Conquer + Memoization)
Không có một “thuật toán” nào cho chúng ta biết một bài toán có thể
giải bằng phương pháp quy hoạch động hay không? Khi gặp một bài toán cụ
thể, chúng ta phải phân tích bài toán, sau đó kiểm chứng ba tính chất của một
bài toán quy hoạch động trước khi tìm lời giải bằng phương pháp quy hoạch
động. Lớp các bài toán giải bằng quy hoạch động là:
• Bài toán tối ưu: Max / Min
• Bài toán đếm
– Đếm số cấu hình
– Thứ tự từ điển
• Bài toán lập bảng phương án
– Xây dựng cấu hình
– Trò chơi
Nếu như bài toán đặt ra đúng là một bài toán quy hoạch động thì việc
đầu tiên phải làm là phân tích xem một bài toán lớn có thể phân rã thành
những bài toán con đồng dạng như thế nào, sau đó xây dựng cách tìm nghiệm
của bài toán lớn trong điều kiện chúng ta đã biết nghiệm của những bài toán
con - tìm công thức truy hồi.
Sau khi xây dựng công thức truy hồi, ta phải phân tích xem tại mỗi
bước tính nghiệm của một bài toán, chúng ta cần lưu trữ bao nhiêu bài toán
con và với mỗi bài toán con thì cần lưu trữ toàn bộ nghiệm hay một phần
nghiệm. Từ đó xác định lượng bộ nhớ cần thiết để lưu trữ, nếu lượng bộ nhớ
cần huy động vượt quá dung lượng cho phép, chúng ta cần tìm một công thức
khác hoặc thậm chí, một cách giải khác không phải bằng quy hoạch động.
Một điều không kém phần quan trọng là phải chỉ ra được bài toán nào
cần phải giải trước bài toán nào, hoặc chí ít là chỉ ra được khái niệm bài toán
này nhỏ hơn bài toán kia nghĩa là thế nào, xác định được một tập các bài toán


2


nhỏ nhất có thể giải trực tiếp, nghiệm của chúng sau đó được dùng làm cơ sở
giải những bài toán lớn hơn.
Trong đa số các bài toán quy hoạch động, nếu bạn tìm được đúng công
thức truy hồi và xác định đúng tập các bài toán cơ sở thì coi như phần lớn
công việc đã xong. Việc tiếp theo là cài đặt chương trình giải công thức truy
hồi:
Cuối cùng là chỉ ra nghiệm của bài toán ban đầu. Nếu bảng phương án
lưu trữ toàn bộ nghiệm của các bài toán thì chỉ việc đưa ra nghiệm của bài
toán ban đầu, nếu bảng phương án chỉ lưu trữ một phần nghiệm, thì trong quá
trình tính công thức truy hồi, ta để lại một “manh mối” nào đó (gọi là vết) để
khi kết thúc quá trình giải, ta có thể truy vết tìm ra toàn bộ nghiệm.
Sau đây tôi sẽ trình bày một số bài toán quy hoạch động cụ thể mà giáo
viên có thể dùng trong quá trình giảng dạy thực tế ở trường cho học sinh:
3. Một số bài toán giải bằng quy hoạch động
3.1 Sub3Sequences
Dãy con của một dãy A cho trước là một dãy thu được bằng cách xóa
đi một số phần tử của dãy A. Mỗi dãy số là dãy con của chính nó. Dãy rỗng
là dãy con của mọi dãy bất kỳ.
Cho trước 3 dãy số A, B, C. Ta nói X là dãy con chung của A, B, C khi
X là dãy con của cả 3 dãy đó.
• Yêu cầu: Cho trước 3 dãy số A, B, C. Tìm dãy con chung có chiều dài
lớn nhất của 3 dãy đó.
• Dữ liệu vào: Cho trong file Sub3Sep.inp gồm 3 dòng, mỗi dòng mô tả
một dãy số.
Dòng đầu tiên ghi các số nguyên ai (|ai|<100 000; 0của các phần tử của dãy A.
Dòng thứ 2 ghi các số nguyên bj (|bj|<100 000; 0

các phần tử của dãy B.
3


Dòng thứ 3ghi các số nguyên c k (|ck|<100 000; 0các phần tử của dãy C.
Các số cách nhau ít nhất bởi một khoảng trắng.
• Dữ liệu ra: ghi ra file Sub3Seq.out gồm một số nguyên L cho biết
chiều dài của dãy con chung dài nhất của 3 dãy đã cho.
Ví dụ:
Sub3Seq.inp
213538594

Sub3Seq.out

Ghi chú: Dãy con
chung dài nhất

4

2389

1 2 3 4 8 9 12 14
4 1 2 3 4 8 12 9 14 9 2



Phân tích:

Đây là bài toán quy hoạch động quen thuộc, nhưng yêu cầu là tìm xâu

con chung dài nhất của 3 xâu chứ không phải 2 xâu mà ta đã biết. Cách giải
bài này tương tự như thuật toán tìm xâu con chung dài nhất của 2 xâu.
- Gọi na, nb, nc lần lượt là chiều dài của các dãy số A, B, C
- Gọi L[i,j,k] là chiều dài dãy con chung dài nhất của 3 dãy con
a1, a2, ....., ai ⊂ A
b1, b2, ....., bi ⊂ B
c1, c2, ....., ci ⊂ C với i=1..na, i=1..nb, k=1..nc
Kết quả ta cần tìm là L[na, nb, nc]
- L[0,0,0]=0; và L[0,j,k] = 0, L[i,0,k] = 0, L[i,j,0] = 0 với i=1..na,
j=1..nb, k=1..nc
4


- Công thức truy hồi:
∀i∈[1..na], ∀j∈[1..nb], ∀k∈[1..nc]
L[i,j,k] := max(L[i-1, j-1, k-1]+ord(A[i]=B[j]) and B[j]= C[k]),
L[i-1, j, k], L[i,j-1,k], L[i,j,k-1]);



Chương trình xử lý:

Program chuongtrinh;
Const max=100;
fo='sub3seq.out';
fi='sub3seq.inp';
Var
f:text;
L:array[0..max, 0..max, 0..max] of longint;
m, n,p,dem: longint;

a, b, c: array [1..max] of longint;
pa: array[1..max] of longint;
Procedure nhap;
begin
m:=0;
n:=0;
assign(f, fi);
reset(f);
5


while not eoln(f) do
begin
inc(m);
read(f,a[m]);
end;
readln(f);
while not eoln(f) do
begin
inc(n);
read(f, b[n]);
end;
readln(f);
while not eoln(f) do
begin
inc(p);
read(f, c[p]);
end;
close(f);
end;

Function lonnhat(x,y,z:longint): longint;
6


var tg: longint;
begin
if (x>y) then tg:=x else tg:=y;
if tg>z then tg:=z;
lonnhat:=tg;
end;
Procedure qhd; //thuat toan quy hoach dong
var i, j, k: longint;
begin
fillchar(L, sizeof(L),0);
for i:=1 to m do
for j:=1 to n do
for k:=1 to p do
if( (a[i]=b[j]) and (b[j]=c[k])) then L[i,j,k]:=L[i-1,j-1, k-1]+1
else L[i,j,k]:= lonnhat(L[i,j,k-1], L[i,j-1,k], L[i-1,j,k]);
// tim cac phan tu cua day con chung trong 3 day A, B, C
dem:=0;
i:=m; j:=n; k:=p;
while (i>0) and (j>0) and (k>0) do
begin
7


if (a[i]=b[j]) and (b[j]=c[k]) then
begin
inc(dem); pa[dem]:=i;

dec(i); dec(j); dec(k);
end
else if L[i,j,k]=L[i-1,j,k] then dec(i)
else if L[i,j,k]= L[i,j-1,k] then dec(j)
else if L[i,j,k]= L[i,j,k-1] then dec(k);
end;
end;
BEGIN
nhap;
qhd;
assign(f,fo);
rewrite(f);
writeln(f, L[m,n,p]);
//for i:=dem downto 1 do write(f, A[pa[dem]], ' ');
close(f);
END.
3.2 Du lịch Đông ↔ Tây (IOI 1993)
8


Bạn là người thắng cuộc trong một cuộc thi do một hãng hàng không
tài trợ và phần thưởng là một chuyến du lịch do bạn tuỳ chọn. Có n thành phố
và chúng được đánh số từ 1 tới n theo vị trí từ Tây sang Đông (không có hai
thành phố nào ở cùng kinh độ), có m tuyến bay hai chiều do hãng quản lý,
mỗi tuyến bay nối giữa hai thành phố trong số n thành phố đã cho. Chuyến du
lịch của bạn phải xuất phát từ thành phố 1, bay theo các tuyến bay của hãng
tới thành phố n và chỉ được bay từ Tây sang Đông, sau đó lại bay theo các
tuyến bay của hãng về thành phố 1 và chỉ được bay từ Đông sang Tây. Hành
trình không được thăm bất kỳ thành phố nào quá một lần, ngoại trừ thành phố
1 là nơi bắt đầu và kết thúc hành trình.



Yêu cầu: Tìm hành trình du lịch qua nhiều thành phố nhất.



Input

 Dòng 1 chứa số thành phố n, và số tuyến bay m
 Dòng tiếp, mỗi dòng chứa thông tin về một tuyến bay: gồm chỉ số hai
thành phố tương ứng với tuyến bay đó.


Output

Hành trình tối ưu tìm được hoặc thông báo rằng không thể thực hiện được
hành trình theo yêu cầu đặt ra.
Ví dụ:
Sample Input
5

6

Sample Output
The best tour:

1 2

Number of cities: 4


2 3

12541

3 4
4 5
1 4
2 5



Phân tích
9


Ta có thể đặt một mô hình đồ thị G = (V, E) với tập đỉnh V là tập các
thành phố và tập cạnh E là tập các tuyến bay. Bài toán này nếu không có
ràng buộc về hành trình Tây–Đông-Tây thì có thể quy dẫn về bài toán tìm
chu trình Hamilton (Hamilton Circuit) trên đồ thị. Nhưng bài toán này nhờ
có ràng buộc về hướng đi nên chúng ta có thể xây dựng một thuật toán cho
kết quả tối ưu với độ phức tạp O(n3). Tại IOI’93*, bài toán du lịch Đông –
Tây này được coi là bài toán khó nhất trong số bốn bài toán của đề thi.
Dễ thấy rằng hành trình đi qua nhiều thành phố nhất cũng là hành trình đi
bằng nhiều tuyến bay nhất mà không có thành phố nào thăm qua hai lần
ngoại trừ thành phố 1 là nơi bắt đầu và kết thúc hành trình. Với hai thành
phố i và j trong đó itheo hướng Tây tới thành phố 1 rồi bay theo hướng Đông tới thành phố j sao
cho không có thành phố nào bị thăm hai lần.
 Nếu tồn tại đường bay như vậy, ta xét đường bay qua nhiều tuyến bay
nhất, ký hiệu (i → 1 →j) và gọi f[i, j] là số tuyến bay dọc theo đường bay

này.
 Nếu không tồn tại đường bay thoả mãn điều kiện đặt ra, ta coi f[i,j ]= -1
Chú ý rằng chúng ta chỉ quan tâm tới các f[i, j] với iphía Tây thành phố j mà thôi.

Nếu tính được các f[i, n] thì tua du lịch cần tìm sẽ gồm các tuyến bay trên
một đường bay (i→1 →n) nào đó ghép thêm tuyến bay (n, i), tức là số tuyến
bay (nhiều nhất) trên tua du lịch tối ưu cần tìm sẽ là max{f[i, n]}+1 . Vấn đề
bây giờ là phải xây dựng công thức tính các f[i, j].

10


Với i < j, xét đường bay (i→1 →j ), đường bay này sẽ bay qua một thành
phố k nào đó trước khi kết thúc tại thành phố j . Có hai khả năng xảy ra:
 Thành phố k nằm phía đông (bên phải) thành phố i (k>i), khi đó
đường bay i→1 →j có thể coi là đường bay i→1 → k ghép thêm
tuyến bay (k, j). Vậy trong trường hợp này f[i, j] = f(k)[i, j] = f[k, i] + 1
 Thành phố k nằm phía tây (bên trái) thành phố i (k < i), khi đó đường
bay i→1 →j có thể coi là đường bay k→1 →i theo chiều ngược lại rồi
ghép thêm tuyến bay (k, j). Vậy trong trường hợp này f[i, j] = f (k)[i, j]
= f[i, k] + 1

Hình: Đường đi từ i→1 →j và hai vị trí của thành phố k
Vì tính cực đại của f[i, j ], ta suy ra công thức truy hồi:
F[i, j] = max {f(k)[i, j]} (1)
trong đó 1<=kCác giá trị f(k)[i, j] trong công thức (1) được tính bởi:

Các phần tử của bảng f sẽ được tính theo từng hàng từ trên xuống và

trên mỗi hàng thì các phần tử sẽ được tính từ trái qua phải. Các phần tử hàng
1 của bảng f (f[1, j]) sẽ được tính trước để làm cơ sở quy hoạch động.
Bài toán tính các giá trị f[1, j] lại là một bài toán quy hoạch động: f[1,
j] theo định nghĩa là số tuyến bay trên đường bay Tây → Đông qua nhiều
tuyến bay nhất từ 1 tới j . Đường bay này sẽ bay qua thành phố k nào đó
11


trước khi kết thúc tại thành phố j. Theo nguyên lý bài toán con tối ưu, ta có
công thức truy hồi:
F[1, j] = max {f[1, k]}+1

(2)

với kCơ sở quy hoạch động để tính các f[1, j] chính là f[1, 1] =0.
Quy trình giải có thể tóm tắt qua hai bước chính:
 Đặt f[1, 1]:=0; và tính các f[1, j] (1 Dùng các f[1, j] vừa tính được làm cơ sở tỉnh các f[i, j] (2<=icông thức (1).
Khi tính f[i, j], đồng thời lưu lại trace[i, j] là thành phố k đứng liền trước j
trên đường bay i→1→j.
 Chương trình xử lý
{$MODE OBJFPC}
program TheLongestBitonicTour;
const
max = 1000;
type
TPath = record
nCities: Integer;

c: array[1..max] of Integer;
end;
var
a: array[1..max, 1..max] of Boolean;
f: array[1..max, 1..max] of Integer;
trace: array[1..max, 1..max] of Integer;
12


p, q: TPath;
n: Integer;
LongestTour: Integer;
procedure Enter; //Nhập dữ liệu
var
i, m, u, v: Integer;
begin
FillChar(a, SizeOf(a), False); //a[ u, v]=t rue ↔ u,v một tuyến bay
ReadLn(n, m);
for i := 1 to m do
begin
ReadLn(u, v); //Tuy n bay hai chiều: a[u, v] = a[v, u]
a[u, v] := True;
a[v, u] := True;
end;
end;
//target := Max(target, value), trả về True nếu target được cực đại hoá.
function Maximize(var target: Integer; value: Integer): Boolean;
begin
Result := value > target;
if Result then target := value;

end;
procedure Optimize; //Giải công thức truy hồi
var
i, j, k: Integer;
13


begin
//Tính các f[1, j]
f[1, 1] := 0; //Cơ sở QHĐ để tính các f[1, j]
for j := 2 to n do
begin //Tính f[1, j]
f[1, j] := -1;
for k := 1 to j - 1 do
if a[k, j] and (f[1, k] <> -1) and
Maximize(f[1, j], f[1, k] + 1) then
trace[1, j] := k; //L ưu vết mỗi khi f[i,j] được cực đại hoá
end;
//dùng các f[i, j] làm cơ sở tính các f[ i, j ]
for i := 2 to n - 1 do
for j := i + 1 to n do
begin
f[i, j] := -1;
for k := i + 1 to j - 1 do //k nằm phía Đông i
if a[k, j] and (f[i, k] <> -1) and
Maximize(f[i, j], f[i, k] + 1) then
trace[i, j] := k;
for k := 1 to i - 1 do //k nằm phía Tây i
if a[k, j] and (f[k, i] <> -1) and
Maximize(f[i, j], f[k, i] + 1) then

trace[i, j] := k;
end;
end;
procedure FindPaths; //Truy vết tìm đường
var
14


i, j, k, t: Integer;
begin
//Tính LongestTour là số tuyến bay nhiều nhất trên đường bay k→1
→n với mọi k
LongestTour := -1;
for k := 1 to n - 1 do
if a[k, n] and Maximize(LongestTour, f[k, n]) then
i := k;
if LongestTour = -1 then Exit;
j := n;
//Tua du lịch cần tìm sẽ là gồm các tuyến bay trên đường bay k
→1→j=n ghép thêm tuyến (i, n)
//Chúng ta tìm đường bay từ Đông→ Tây p: i→1 và q: j→ 1 gồm các
tuyến bay trên đường bay i→ 1 →j
p.nCities := 1; p.c[1] := i; //L ưu trữ thành phố cuối cùng của đường
p
q.nCities := 1; q.c[1] := j; //Lưu trữ thành phố cuối cùng của đường q
repeat
if i < j then //Truy vết đường bay i→1 →j
with q do
begin
k := trace[i, j]; //Xét thành phố k đứng liền trư ớc j

Inc(nCities); c[nCities] := k; //đưa k vào đường q
j := k;
end
else //Truy vết đường bay j→1 →i
with p do
begin
k := trace[j, i]; //Xét thành phố k đứng liền trước i
15


Inc(nCities); c[nCities] := k; // đưa k vào đương p
i := k;
end;
until i=j; //khi i=j thì chắc chắn i=j=1
end;
procedure PrintResult; //In kết quả
var
i: Integer;
begin
if LongestTour = -1 then
WriteLn('NO SOLUTION!')
else
begin
WriteLn('The best tour: ');
WriteLn('Number of cities: ', LongestTour + 1);
// Để in ra tua du lịch tối ưu, ta sẽ in ghép đường:
//In ngược đường p để có đ ừơng đi T ây → Đông: 1 →i
//In xuôi q để có đường đi Đông→ Tây: n→ 1
for i := p.nCities downto 1 do Write(p.c[i], ' ');
for i := 1 to q.nCities do Write(q.c[i], ' ');

end;
end;
BEGIN
Enter;
Optimize;
FindPaths;
16


PrintResult;
END.

3.3.3 Chọn ô - HSG QG 2006 (SELECT)
Cho một bảng hình chữ nhật kích thước 4×n ô vuông. Các dòng được
đánh số từ 1 đến 4, từ trên xuống dưới, các cột được đánh số từ 1 đến n từ trái
qua phải.
Ô nằm trên giao của dòng i và cột j được gọi là ô (i,j). Trên mỗi ô (i,j)
có ghi một số nguyên aij , i =1, 2, 3, 4; j =1, 2, ..., n. Một cách chọn ô là việc
xác định một tập con khác rỗng S của tập tất cả các ô của bảng sao cho không
có hai ô nào trong S có chung cạnh. Các ô trong tập S được gọi là ô được
chọn, tổng các số trong các ô được chọn được gọi là trọng lượng của cách
chọn. Tìm cách chọn sao cho trọng lượng là lớn nhất.
Ví dụ: Xét bảng với n=3 trong hình vẽ dưới đây:

Cách chọn cần tìm là tập các ô S = {(3,1), (1,2), (4,2), (3,3)} với trọng lượng
32.
Dữ liệu: SELECT.INP
 Dòng đầu tiên chứa số nguyên dương n là số cột của bảng.
 Cột thứ i trong số 4 dòng tiếp theo chứa n số nguyên là n số trên hàng i
của bảng.

Kết quả: SELECT.OUT
17


 Gồm 1 dòng duy nhất là trọng lượng của cách chọn tìm được.
Hạn chế:
 Trong tất cả các test: n ≤ 10000.
 |aij| ≤ 30000.
Ví dụ:
SELECT.INP SELECT.OUT
3

32

-1 9 3
-4 5 -6
7 89
9 72
Gợi ý: Mỗi cột có 4 dòng nên có thể dùng biến x có 4 bit nhị phân để mô tả
trạng thái chọn của cột i: bit thứ k bằng 1 (hoặc 0) tương ứng với ô dòng thứ
k của cột được (không được) chọn.
Gọi F[i,x] là trọng lượng lớn nhất nếu xét từ cột 1 đến cột i và trạng
thái chọn của cột i được biểu diễn bằng biến x. Công thức Quy hoach động
là:
F[i,x] = max ( F[i-1,x’] + sum(i,x) )
Trong đó
-

x và x’ là hai trạng thái chọn của 2 cột liên tiếp nhau (i và i-1) do
đó 2 trạng thái phải thỏa mãn điều kiện không có 2 ô nào được

chọn kề nhau.

-

sum(i,x) là trọng lượng ứng với trạng thái chọn x của cột i.

3.3.4 Số lớn nhất
Cho 2 xâu:
18


X = x1x2..xm. (Với xi là các kí tự số từ ‘0’ đến ‘9’)
Y = y1y2..yn.( Với yj là các kí tự số từ ‘0’ đến ‘9’) (m, n <= 250)
Ta gọi: Z = z1z2..zk là xâu chung của 2 xâu X, Y nếu xâu Z nhận được từ xâu
X bằng cách xoá đi một số kí tự và cũng nhận được từ xâu Y bằng cách xoá
đi một số kí tự.
Yêu cầu: Tìm một xâu chung của 2 xâu X, Y sao cho xâu nhận được tạo
thành một số lớn nhất có thể.
Dữ liệu vào file: String.inp
Gồm 2 dòng, dòng 1 là xâu X, dòng 2 là xâu Y.
Kết quả ra file: String.out
- Nếu không có cách xóa thì ghi số -1
- Nếu có cách xóa thì ghi số Z lớn nhất có thể nhận được.
Ví dụ:
String.inp

String.out

19012304
034012


34

 Gợi ý:
-

Tìm dãy con chung dài nhất bằng quy hoạch động: gọi L[i, j] là
độ dài của dãy con chung dài nhất khi dãy X gồm các phần tử
tử i tới length(x), dãy thứ 2 gồm các phần tử từ j tới length(y),
ta có: L[i,j]:= max(L[i+1,j+1] + ord(X[i]=Y[j]), L[i, j+1],
L[i+1, j])

-

Tìm chữ số ở hàng trái nhất (cao nhất) đó là chữ ch có vị trí i
trong xâu X và vị trí j trong xâu Y mà m=L[i,j] (độ dài xâu con
chung dài nhất là m)

-

Tìm tiếp các hàng tiếp theo:
19


Tìm chữ số k nhận giá trị m-1 tới 1 (tính từ bên phải dãy con
chung sang trái)
{
Duyệt chữ số ch từ 9 tới 1
{ cho u tăng từ i+1 cho tới khi x[u]=ch
Cho v tăng từ j+1 tới khi y[v]=ch

Nếu L[u, v]= k thì ch là chữ số hàng k (tính
từ phải sang)
}}
Chú ý: Với bài toán trên ta cũng có thể hỏi:
Hãy tìm xâu con chung của X và Y sao cho xâu con nhận được có trọng
lượng lớn nhất.
Trọng lượng của một xâu Z tùy ý với Z = z 1z2.....zn (z1, z2, ..., zn là các số
nguyên) được tính bằng z1+z2+ ...+zn
Gợi ý: Gọi W[i, j] là trọng lượng lớn nhất của xâu con chung của 2 xâu X và
Y. Với 0<= i<= m và 0<= j <= n ta có:

3.3.5 Phép chia
Cho biểu thức A= x1/x2/x3/.../xn
Với xi (1<= i <= n) là các số dương, và kí hiệu “x k / xj” thể hiện cho phép
chia hai số xk và xj.
Hãy tìm một cách đặt cặp dấu ngoặc vào trong biểu thức A sao cho biểu thức
chỉ với các phần tử trong ngoặc đó có giá trị lớn nhất.
Ví dụ: A= 3/2/0.25/0.5 thì P = 3/(2/0.25/0.5) là biểu thức sau khi đặt cặp dấu
ngoặc từ biểu thức A. Và 2/0.25/0.5 có giá trị lớn nhất so với các biểu thức
3/2, 3/2/0.25, 0.25/0.5 .....
20




Gợi ý:

Gọi Xi...j là biểu thức chứa xi/x+1/.../ xj với 1≤i ≤ j ≤ n.
Pi..j là biểu thức của Xi..j sau khi thêm cặp dấu ngoặc vào.
C(Pi..j) là giá trị của biểu thức Pi..j

Với X1..n sẽ có thể tách ra làm 2 dãy con là X1..k và Xk+1...n với 1≤ k ≤ n-1
C ( P1..k )

Khi đó C(P1..n) = C ( P )
k +1.. n
Sử dụng thuật toán quy hoạch động:
Gọi M[i, j] là giá trị lớn nhất của một biểu thức được tạo ra từ X i..j khi đặt cặp
dấu ngoặc vào một vị trí nào đó trong đoạn này.
Gọi m[i, j] là giá trị nhỏ nhất của một biểu thức được tạo ra từ X i..j khi đặt cặp
dấu ngoặc vào một vị trí nào đó trong đoạn này.
Ta sẽ có:

Các bạn đồng nghiệp có thể tham khảo cách cài đặt cũng như một số bài toán
quy hoạch động hay khác trong địa chỉ sau: />sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=4&ved=0CD8QFjAD&ur
l=http%3A%2F%2Fwww.dei.unipd.it%2F~geppo%2FDA2%2FDOCS
%2Fdynprog.pdf&ei=Okg4UMTZGYTJrQfEoDADA&usg=AFQjCNFxBRcuNdrC5S3YB2P2sD5S1byGCQ
4. Kết luận
Quy hoạch động là một trong những thuật toán quan trọng hay có trong
các đề thi học sinh giỏi quốc gia và quốc tế. Tuy nhiên một bài toán có thể
có nhiều cách giải, và cũng không có một quy tắc hay một định hướng cụ
21


thể nào là với những bài toán nào thì giải bằng quy hoạch động. Tất cả
phải dựa vào đặc trưng của từng bài toán cũng như kinh nghiệm của người
làm.
Hi vọng bài viết này hữu ích đối với các bạn đồng nghiệp trong quá
trình dạy quy hoạch động cho học sinh. Vì thời gian có hạn cũng như kinh
nghiệm giảng dạy còn ít nên tôi mới trình bày thuật toán ở mức cơ bản,
việc cài đặt thuật toán có thể chưa tối ưu. Rất mong các bạn đồng nghiệp

góp ý để bài viết này sẽ được hoàn thiện hơn.

22



×