1. Trang chủ
  2. » Luận Văn - Báo Cáo

CHUYÊN đề PHƯƠNG PHÁP QUY HOẠCH ĐỘNG

12 394 1

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 12
Dung lượng 84 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

CHUYÊN ĐỀ PHƯƠNG PHÁP QUY HOẠCH ĐỘNG Quy hoạch động – giống như phương pháp chia để trị - giải quyết các bài toán bằng cách kết hợp các giải pháp của các bài toán con.. Điểm khác biệt là

Trang 1

CHUYÊN ĐỀ PHƯƠNG PHÁP QUY HOẠCH ĐỘNG

Quy hoạch động – giống như phương pháp chia để trị - giải quyết các bài toán bằng cách kết hợp các giải pháp của các bài toán con Điểm khác biệt là một thuật toán quy hoạch động giải quyết các bài toán con đúng một lần và sau đó ghi kết quả của chúng trong một bảng, như vậy tránh được việc phải tính lại các kết quả khi bài toán con được lặp lại

1 Nguyên lý tối ưu Bellman

Quy hoạch động là quá trình điều khiển tối ưu với trạng thái bắt đầu Mọi trạng thái A bất kỳ thuộc quá trình này cũng tối ưu

Tư tưởng chính: Thực hiện các bài toán con trước, sử dụng các kết quả này

để giải bài toán lớn

Hướng tiếp cận:

- Giải bài toán theo công thức truy hồi

- Giải các bài toán con trước

- Dựa vào bài toán con để giải bài toán lớn hơn cho đến khi gặp bài toán cần giải

Tổng quát:

Giải bài toán qua N bước

Giải bài toán sao cho tổng chi phí 1N-1 và N-1 N là tối ưu

2 Thuật toán quy hoạch động

Bước 1: Xây dựng công thức truy hồi

Giả sử: Chia bài toán thành N giai đoạn 1->N

Xác định F(1) - cơ sở quy nạp

- Gọi (i) là hàm số xác định giá trị tối ưu, tính đến giá trị thứ i

 F(n) là nghiệm của bài toán

- Đối với bài toán

+ Tìm Min: F(N) = Min{F(i), F(j)}

i = 1, N-1

j = N-1, N

Trang 2

+ Tìm Max: F(N) = Max{F(i), F(j)}

i = 1, N-1

j = N-1, N

Bước 2: Tìm cơ sở quy hoạch động

Để chia bài toán lớn thành các bài toán con có cùng một cách giải thì yêu cầu phải biến đổi một số bài toán con ở trường hợp cơ sở quy nạp bằng cách thêm biến phụ, chính là cơ sở quy hoạch động

Bước 3: Lấp đầy bảng phương án, dựa vào cơ sở quy hoạch động, công thức

quy hoạch động

Giải bài toán

Bước 4: Truy vết

Tìm nghiệm của bài toán

3 Một số ví dụ minh họa

Bài 1 Dãy con không giảm dài nhất

Cho N số nguyên dương (0<N≤104) và một dãy A1, A2,…, AN các số nguyên Tìm trong dãy đã cho một dãy con:

thỏa các điều kiện sau:

a

b ij<ij+1

c k lớn nhất

Dữ liệu vào: File BT1.INP có cấu trúc như sau:

- Dòng thứ nhất ghi số N

- Các dòng tiếp theo chứa dãy A1, A2,…, AN mỗi dòng 10 số (trừ dòng cuối có thể ít hơn 10 số), các số cách nhau ít nhất một dấu cách

Dữ liệu ra: File BT1.OUT có cấu trúc như sau:

- Dòng thứ nhất ghi số k

- Các dòng tiếp theo chứa k số Ai1, Ai2, , Aik mỗi dòng 10 số (dòng cuối cùng có thể ít hơn 10 số), các số cách nhau ít nhất một dấu cách

Hướng dẫn: Xây dựng mảng T và D có ý nghĩa sau:

- T[i]=j mà chỉ số j trong mảng A của phần tử đứng ngay trước Ai

trong dãy kết quả Cách tính T[i]: duyệt mảng A từ vị trí 1 đến vị trí i-1, tìm

vị trí j có D[j] lớn nhất trong số các vị trí j thỏa mãn A[j]<= A[i]

k

i i i

A , , , ,

3 2 1

1

j

j i

A

Trang 3

- D[i] là độ dài của dãy kết quả khi bài toán xét dãy A1, A2,…, Ai và được tính theo công thức truy hồi:

D[i] = Max { D[j] +1  j mµ j<i vµ AjAi }

Chú ý khởi trị: T[1]=0 và D[1]=1

- Khi duyệt ngược tìm kết quả, ta tiến hành như sau:

Tìm phần tử lớn nhất trong D, giả sử đó là D[i], ta đổi dấu D[i] coi như đánh dấu nó, tìm tiếp phần tử j= T[i], lại đổi dấu D[j], quá trình sẽ như thế lùi chỉ số j đến khi j=0

Chương trình

uses crt;

const max = 10000;

fi = 'BT1.INP';

fo = 'BT1.OUT';

type mang = array[1 max] of integer;

var a,d,t : mang;

n,dem : longint;

f : text;

procedure nhap;

var i: longint;

begin

fillchar(a,sizeof(a),0);

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do read(f,a[i]);

close(f);

end;

procedure khoitri;

begin

fillchar(t,sizeof(t),0);

t[1]:= 0;

d[1]:= 1;

end;

function vitri_truoc(i: longint): longint;

var j,ld,lj: longint;

begin

ld:= 0;

lj:= 0;

vitri_truoc:= 0;

for j:=i-1 downto 1 do

if a[j]<=a[i] then

if d[j]>ld then

begin

ld:= d[j];

lj:= j;

end;

vitri_truoc:= lj;

end;

Trang 4

procedure tao_td;

var i: longint;

begin

khoitri;

for i:=2 to n do

begin

t[i]:= vitri_truoc(i);

if t[i]=0 thend[i]:= 1

else

d[i]:= d[t[i]]+1;

end;

end;

function maxd: longint;

var i,p,li: longint;

begin

p := -maxint;

li := 0;

for i:=1 to n do

if d[i]>p then

begin

p := d[i];

li := i;

end;

maxd:= li;

end;

procedure timketqua;

var i: longint;

begin

i := maxd;

d[i] := -d[i];

dem := 1;

repeat

i:= t[i];

if i<>0 then

begin

d[i]:= -d[i];

inc(dem)

end;

until i=0;

end;

procedure ghi;

var f : text;

i : longint;

begin

assign(f,fo);

rewrite(f);

writeln(f,dem);

dem:= 0;

for i:= 1 to n do

Trang 5

if d[i]<0 then

begin write(f,a[i]:7);

if dem mod 10 = 0 then writeln(f);

end;

close(f);

end;

BEGIN

clrscr;

nhap;

tao_td;

timketqua;

ghi;

END

Bài 2 Cho hai dãy số nguyên (a1,a2, ,am), (b1,b2, ,bn) 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)

Lời giải

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ợp1: 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 (a1,a2, ,ai), (b1,b2, ,bj) với 0 <= i <=

m, 0 <= j <= n Gọi l[i,j] là độ dài của dãy con chung lớn nhất của hai dãy (a1, ,ai), (b1, ,bj) ; Như vậy ta phải tính tất cả các l[i,j] trong đó 0<=i<=m, 0<=j<=n

Chúng ta có thể thấy ngay rằng l[0,0]=0

- Nếu ai # bj thì l[i, j]=max{l[i-1, j], l[i, j-1]}

Trang 6

- Nếu ai=bj thì l[i, j]= 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 (a1, am), (b1, bn)

Để 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[i,j] Nếu ai=bj thì ta thêm ai vào dãy con rồi nhảy tới ô l[i-1,j-1] Nếu ai # bj thì l[i, j]=l[i-1,j] hoặc l[i,j]=l[i, j-1] Nếu l[i,j]=l[i-1,j] thì nhảy tới ô l[i-1,j], ngược lại thì nhảy tới ô l[i,j-1]

Sau đây là lời giải của bài toán Chương trình được viết bằng ngôn ngữ Pascal:

uses crt;

const

fi='b2.inp';

var

a: array[1 10] of integer;

b: array[1 10] of integer;

kq: array[0 10,0 10] of integer;

i, j, maxa, maxb: integer;

f: text;

procedure init;

begin

assign(f,fi);

reset(f);

i:=0;

while not(eoln(f)) do

begin

inc(i);

read(f,a[i]);

end;

maxa:=i;

readln(f);

i:=0;

while not(eoln(f)) do

begin

inc(i);

read(f,b[i]);

Trang 7

end;

maxb:=i;

close(f);

end;

function max(a,b:integer):integer;

begin

if a>b then max:=a

else max:=b;

end;

begin

init;

kq[0,0]:=0;

for i:=1 to maxa do

for j:=1 to maxb do

if a[i]<>b[j] then kq[i,j]:=max(kq[i-1,j],kq[i,j-1])

else kq[i,j]:=kq[i-1,j-1]+1;

writeln('Do dai day con chung lon nhat:', kq[maxa,maxb]);

i:=maxa;

j:=maxb;

while (i>0)or(j>0) do

if a[i]=b[j] then

begin

write(a[i]);

dec(i);

dec(j);

end

else

if kq[i-1,j]=kq[i,j] then dec(i)

else dec(j);

end

Bài 3 Bài toán chia kẹo

Có N gói kẹo, gói thứ i có Ai cái kẹo Không được bóc bất kỳ một gói kẹo nào, cần chia N gói kẹo thành hai phần sao cho độ chênh lệch số kẹo giữa hai gói là ít nhất

Dữ liệu vào trong file " chiakeo.inp" có dạng :

Trang 8

- Dòng đầu tiên là số N(N<=100);

- Dòng thứ hai là N số Ai(i=1, 2, , N; Ai <=100)

Kết quả ra file " chiakeo.out" có dạng:

- Dòng đầu là độ chênh lệch nhỏ nhất giữa hai phần có thể được

- Dòng hai là một dãy N số, nếu si =1 thì gói thứ i thuộc phần 1, nếu

si=2 thì gói thứ i thuộc phần 2

Thuật toán:

Với một số M bất kì, nếu ta biết được có tồn tại một cách chọn các gói kẹo để tổng số kẹo của các gói được chọn bằng đúng M không, thì bài toán

sẽ được giải quyết Vì đơn giản là ta chỉ cần chọn số M sao cho M gần với

Ai/2 nhất (với i =1,2, ,N) Sau đó xếp các gói kẹo để tổng bằng M vào phần một, phần thứ hai sẽ gồm các gói kẹo còn lại Để kiểm tra được điều trên ta

sẽ xây dựng tất cả các tổng có thể có của N gói kẹo bằng cách: ban đầu chưa

có tổng nào được sinh ra Làm lần lượt với các gói kẹo từ 1 đến N, với gói kẹo thứ i, ta kiểm tra xem hiện tại có các tổng nào đã được sinh ra, giả sử các tổng đó là x1, x2, , xt vậy thì đến bước này sẽ có thể sinh ra các tổng x1,

x2, , xt và Ai và x1+Ai, x2+Ai, , xt+Ai.Với N gói kẹo, mà mỗi gói có không quá 100 cái kẹo vậy tổng số kẹo không vượt quá N*100 <= 10000 cái kẹo Dùng mảng đánh dấu D, nếu có thể sinh được ra tổng bằng k thì D[k] = 1 ngược lại D[k] = 0

* Chương trình

{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q+,R+,S+,T-,V+,X+,Y+}

{$M 16384,0,655360}

Program chia_keo;

uses crt;

const max = 100;

fi ='chiakeo.inp';

fo ='chiakeo.out';

var a,s : array[1 max]of integer;

d1,d2,tr : array[0 max*max]of integer;

n,m,sum : integer;

Procedure docf;

var f: text;

k : integer;

begin

assign(f,fi); reset(f);

readln(f,n);

Trang 9

sum:=0;

for k:=1 to n do

begin

read(f,a[k]);

sum:=sum+a[k];

end;

close(f);

end;

Procedure lam;

var i,j : integer;

Begin

fillchar(d1,sizeof(d1),0);

fillchar(tr,sizeof(tr),0);

d1[0]:=1;d2:=d1;

for i:=1 to n do

begin

for j:=0 to sum-a[i] do

if (d1[j]=1)and(d2[j+a[i]]=0) then

begin

d2[j+a[i]]:=1;

tr[j+a[i]]:=i;

end;

d1:=d2;

end;

end;

Procedure ghif;

var m,k : integer;

f :text;

Begin

fillchar(s,sizeof(s),0);

m:=sum div 2;

while d2[m]=0 do dec(m);

assign(f,fo);

rewrite(f);

writeln(f,sum-2*m);

while tr[m]>0 do

begin

s[tr[m]]:=1;

m:=m-a[tr[m]];

end;

Trang 10

for k:=1 to n do

if s[k]=1 then write(f,s[k],#32) else write(f, 2,#32);

close(f);

end;

BEGIN {main}

docf;

lam;

ghif;

END

4 Bài tập ứng dụng

Bài 1 Dãy con có tổng bằng S:

Cho dãy a1,a2, an Tìm một dãy con của dãy đó có tổng bằng S

Hướng dẫn

Đặt L[i,t)=1 nếu có thể tạo ra tổng t từ một dãy con của dãy gồm các phần tử a1, a2, , ai Ngược lại thì L[i,t)=0 Nếu L[n,S)=1 thì đáp án của bài toán trên là “có”

Ta có thể tính L[i,t] theo công thức: L[i,t]=1 nếu L[i–1,t]=1 hoặc L[i– 1,t–a[i]]=1

Cài đặt

Nếu áp dụng luôn công thức trên thì ta cần dùng bảng phương án hai chiều Ta có thể nhận xét rằng để tính dòng thứ i, ta chỉ cần dòng i–1 Bảng phương án khi đó chỉ cần 1 mảng 1 chiều L[0 S] và được tính như sau: L[t]:=0; L[0]:=1;

for i := 1 to n do

for t := S downto a[i] do

if (L[t]=0) and (L[t–a[i]]=1) then L[t]:=1;

Bài 2 Bắc cầu

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

Trang 11

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 toá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

Bài 3.

Tại thời điểm 0, ông chủ 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 n khá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ụng máy từ thời điểm di đến thời điểm

ci (di, ci là 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 gian sử dụng máy của hai khách được nhận phục vụ bất kỳ không được giao nhau đồng thời tổng tiền thu được từ việc phục vụ họ là lớn nhất

Dữ liệu vào: Từ file văn bản THUE.INP

- Dò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ết quả: 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àng nhậ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ày chúng ta phải chú ý ở chỗ: Để dùng thuật toán Quy hoạch động tối ưu từng bước thì 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

Trang 12

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 đến k.

Với mỗi F[k] ta có:

- 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 quy hoạ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

Để 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[i] là chỉ số của khách hàng được chọn trước khách hàng i sao cho tổng tiền thu được là lớn nhất và thỏa mãn các yêu cầu bài toán

Ngày đăng: 14/10/2015, 14:41

TỪ KHÓA LIÊN QUAN

w