Thuật toán quan hệ động mảng một chiều
Trang 1Thuật toán quy hoạch động trên mảng một chiều
Trần Minh Quang
Bài toán 1: Cho một dãysố nguyên dương a1, a2, aN Hãy tỉa bớt mộtsố ít nhất các phần
tử của dãy số nguyên đó và giữ nguyên thứ tự các phần tửcòn lại sao cho dãy số còn lại là một dãy tăng dần Ta gọi dãy số nguyên tăngdần còn lại sau khi đã tỉa bớt một số phần tử
là dãy con của dãy đã cho
Input: Dữ liệu vào được cho bởi tệp văn bản với quy cách:
- Dòng đầu ghi số N là sốphần tử
- Dòng tiếp theo ghi N sốlà các số nguyên của dãy
Output:
-Ghi ra màn hình: Số lượng phần tử của dãy con cực đại và chỉ số các phần tửtrong dãy con đó (theo thứ tự tăng dần)
Ví dụ:
- Với Input trong fileDAYSO.INP như sau:
10
10 100 20 1 2 50 70 80 3 60
- thì Output phải là:
1 2 50 70 80
ý tưởng của thuật toánquy hoạch động ở đây là: Để xây dựng dãy con dài nhất của dãy đã cho chúngta sẽ xây dựng dãy con dài nhất của đoạn phần tử đầu a1, a2, ai
Để làm được điều đó: ta gọi S[i] là số lượng phần tử nhiều nhất của dãycon tăng dần, trong đó ai cũng thuộc dãy con trên (nó là phần tửcuối cùng)
Chúng ta sẽ tính S[i] ở từng bước dựa vào các S[i-1], S[1] như sau:
Trang 2S[i]:=Max(S[j]+1) với j=i-1, 1 mà aj < ai.
Để lấy lại dãy con cực đại ta dùng một mảng Truoc với ý 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 a1,a2, ai
Bây giờ chúng ta phải tìm vị trí i sao cho S[i] đạt max Ta lưu vị trí đóvào biến Luu
Như vậy: S[Luu] chính là số lượng phần tử của dãy con cực đại của dãy đãcho Và bằng mảng Truoc ta có thể lấy lại chỉ số các phần tử thuộc dãy con đó
Đến đây ta gặp một vấn đề: Mảng Truoc chỉ cho phép ta lần ngược từ cuốivề đầu dó đó
để in ra các chỉ số theo thứ tự tăng dần ta phải dùng thêm mộtmảng phụ P và in ngược lại của mảng P:
dem:=0;
i:=Luu;
While i<>0 do
Begin
Inc(dem);P[dem]:=i;i:=Truoc[i];
End;
Chỉ số theo thứ tự tăng dần của dãy con cực đại được in ra bằng dònglệnh:
For i:=dem downto 1 do Write(P[i],' ');
Tuy nhiên làm như trên có vẻ dài dòng trong khi chúng ta đã nhận ra tínhđệ quy trong việc lấy ngược lại Và thủ tục in ra dãy con đã rất ngắn gọn vàsáng sủa:
Procedure Print(i:Integer);
Begin
If i>0 then
Begin
Print(Truoc[i]);Write(i,' ');
End;
End;
Trang 3Công việc in ra chỉ cần một lời gọi: Print(Luu);
Ta có toàn văn chương trình:
{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+,Y+} {$M 65500,0,655360}
Uses Crt;
Const fi = 'DAYSO.INP';
MaxN=5000;
Var A : Array[1 MaxN] ofInteger;
S :Array[1 MaxN] of Integer;
Truoc : Array[1 MaxN]of Integer;
i,j,Luu : Word;
N : Word;
Procedure Init;
Begin
Fillchar(S,SizeOf(S),1);
Fillchar(Truoc,SizeOf(Truoc),0);
End;
Procedure Readfile;
Var f:Text;
Begin
Assign(f,fi);
Trang 4For i:=1 to N do Read(f,A[i]); Close(f);
End;
Procedure Find;
Begin
For i:=2 to N do
Begin
For j:=i-1 downto 1 do
If (A[j]< and (S[i]
Begin
S[i]:=S[j]+1;
Truoc[i]:=j;
End;
End;
End;
Procedure Print(i:Word); Begin
If i >0 then
Begin
Print(Truoc[i]);
Write(a[i],' ');
End;
End;
Trang 5Procedure OutputResult;
Begin
Luu:=N;
For i:=N-1 downto 1 do
If S[i]>S[Luu] then Luu:=i;
Print(Luu);
End;
BEGIN
Clrscr;
Init;
Readfile;
Find;
OutputResult;
Readln;
END
Qua ví dụ trên chúng ta đã hiểu cách mà thuật toán thể hiện Bây giờchúng ta sẽ xét tiếp một bài toán sắp xếp trình tự phục vụ khách hàng mà cáchgiải đều sử dụng thuật toán Quy hoạch động trên mảng một chiều
Ta xét tiếp mộtví dụ sau:
Bài toán 2: Tại thời điểm 0, ôngchủ một máy tính hiệu năng cao nhận được đơn đặt hàng
thuê sử dụng máy của nkhách hàng Các khách hàng được đánh số từ 1 đến n Khách hàng i cần sử dụngmáy từ thời điểm di đến thời điểm ci (di, cilà các số nguyên và 0 < di <
ci < 1000000000) và sẽ trả tiền sử dụng máy là pi (pi nguyên, 0 < p i ≤ 10000000) Bạn cần xác định xem ông chủ cần nhận phục vụ những khách hàng nào sao cho khoảng thời
Trang 6Dòng đầu tiên ghi số n (0 < n =< 1000);
- Dòng thứ i+1 trong số n dòng tiếp theo ghi 3 số di, ci,pi cách nhau bởi dấu trắng (i = 1, 2, n)
Kếtquả: Ghi ra file văn bản THUE.OUT
-Dòng đầu tiên ghi hai số nguyên dương theo thứ tự là số lượng khách hàngnhận phục vụ
và tổng tiền thu được từ việc phục vụ họ
-Dòng tiếp theo ghi chỉ số của các khách hàng được nhận phục vụ
Ví dụ:
3
150 500 150
1 200 100
400 800 80
2 180
2 3
4
400 821 800
200 513 500
100 325 200
600 900 600
2 1100
2 4
Bài toán nàychúng ta phải chú ý ở chỗ: Để dùng thuật toán Quy hoạch động tối ưu từng bướcthì trước hết chúng ta phải sắp xếp các ci theo thứ tự tăng dần:
Giả sử c1 ≤ c2 ≤ ≤ cN
Tương tự bàitoán trên: Gọi F[k] là số tiền lớn nhất khi phục vụ một số khách hàng từ 1 đếnk
Với mỗi F[k] tacó:
- Nếu chấp nhận phục vụ khách k thìF[k]:=F[t]+pk (với t là chỉ số max thoả mãn khoảng thời gian [dt, ct [dk,ck]= )
- Nếu không chấp nhận phục vụ k thì F[k]:=F[k-1]
Như vậy hàm quyhoạch động của F[k] sẽ là:
F[k]:=Max{F[t]+pk,F[k-1]} với k = 2, 3, N và t có ý nghĩa như trên
Trang 7Để lấy lại chỉ số các khách hàngđược phục vụ chúng ta lại dùng mảng Truoc như ví dụ trên
Trên đây là những gì tôi muốn trìnhbày với các bạn Theo tôi, thuật toán tuy đơn giản nhưng tầm ứng dụng của nórất phong phú mà nếu nắm vững nó là rất có lợi cho tư tưởng thuật toán của cácbạn