1. Trang chủ
  2. » Công Nghệ Thông Tin

Bài toán nhân ma trận và Quy hoạch động

5 6,5K 97
Tài liệu đã được kiểm tra trùng lặp

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 5
Dung lượng 60 KB

Nội dung

Bài toán nhân ma trận và Quy hoạch động

Trang 1

Quy hoạch động với bài toán nhân ma trận

Đỗ Quốc Trí

Như ta đã biết quy hoạch động là một phương pháp phổ biến để giải các bài toán, và nếu bạn đã học lập trình thì không thể không biết đến phương pháp này Trong bài viết tôi muốn đề cập đến phương pháp này với bài toán nhân ma trận đơn giản tiến tới áp dụng giải các bài toán thực tế phức tạp hơn

Cho ma trận A có kích thước p*q và ma trận B có kích thước q*r thực hiện phép nhân ma trận để có ma trận C có kích thước p*r

C[i,j] = ồ A[i,k]*B[k,j] (k : 1 ->q ; 1 <= i <= p , 1 <= j <= q)

Vi dụ:

A * B = C

Ma trận A có kích thước 3*4, Ma trận B có kích thước 4*5, Ma trận C có kích thước 5*3

Để nhân ma trận ta Ăp*q) voi B (q*r) ta thực hiện chương trình sau

For i := 1 to p do

For j := 1 to r do

Begin

C[i,j] := 0 ;

For k := 1 to q do C[i,j] := C[i,j] + A[i,k] * A[k,j] ;

end ;

Dễ dàng thấy để nhân 2 ma trận này với nhau ta cần phải dùng p *q* r phép nhân, ta gọi đây là phí tổn để nhân 2 ma trận này

Vấn đề đặt ra là nhân một dãy ma trận M1 , M2 , M3 …Mn ;

Ta chú ý là phép nhân ma trận khôngcó tính chất giao hoán nhưng có tính chất kết hơp

Ví dụ (A* B)*C = A*(B*C) ;

Dữ liệu vào File MATRAN.INP

Trang 2

Dòng đầu ghi n

Dòng thứ 2 ghi n+1 so a[1] a[n+1] với ý nghĩa kích thước của ma trận M[i] là

a[i]*a[i+1]

Dữ liệu ra File MATRAN.OUT

Ghi số n là số phép nhân cần thực hiện

Ví dụ:

Bây giờ ta phân tích bài toán

Nếu n = 1 thì số phép nhân sẽ là 0;

Nếu n = 2 thì ta cung tính được số phép nhân như đã nói ở trên

Xét n >=3:

Ta gọi F[i,j] là chi phí cần dùng để nhân dãy ma trận M[i] , M[i+1] M[j]

Để nhân dãy ma trận này ta có cách kết hợp sau:

M[i]* M[i+1]*… M[j] = (M[i]* M[i+1]*…M[k])*( M[k+1]* M[k+2]*… M[j]) (i≤k<j)

Nếu ta biết F[i,k] và F [k+1,j] với mọi i ≤ k < j thì ta sẽ tính được F [i,j] theo công thức

F [i,j]:= min(F [i,k] * F [k+1,j]) với mọi i≤k<j

Vậy ta có thể tính được công thức truy hồi bằng chương trình quy hoạch động sau: Procedure Optimize ;

Begin

For i := 1 to n do

For j := 1 to n do

IF i <> j then F [i,j] := maxlongint

Else F [i,j] := 0 ; {F[i,i] = 0 với mọi i }

For l := 1 to n-1 do {l là độ dài của dãy}

For i := 1 to n - l do

Begin

j := i+l ;

For k := i to j-1 do {k chạy từ i tới j-1}

if F [i,j] > F [i,k] + F [k+1,j] + a[i] * a[k+1] * a[j+1] then

F[i,j] := F[i,k] + F[k+1,j] + a[i] * a[k+1] * a[j+1] ;

{tối ưu F[i,j]}

End;

End;

Và ta chỉ cần viết ra output là F[1,n]

Nếu đề bài bắt ta truy vết thì ta chỉ cần lưu thêm mảng Trace với Trace[i,j] = k ;

Bây giờ là một bài toán áp dụng của bài toán này

Cho mảng gồm 3 phần tử a, b, c ta xác định phép toán nhân sau

Trang 3

chẳng hạn ta có aa = b ; ac = c ; chú ý ở đây không có tính kết hợp và giao hoán

Bài toán đặt ra là cho 1 sâu có độ dài không quá 100, hãy tìm cách đặt dấu ngoặc để thể hiện phép nhân sao cho kết quả là a

Input GIATRI.INP: ghi sâu S

Output GIATRI.OUT nếu không đạt được thì ghi 0 nếu đạt được thì ghi cách đặt

Ví dụ:

Trước tiên ta phân tích bài toán

Khởi tạo mảng F[i,j,u] với 1<=i, j <= 100, u = ’a’ ’c’ kiểu Boolean với ý nghĩa

Có thể biến đổi được từ kí tự i đến kí tự j thành k được không (F[i,j] = true nếu được,

= false nếu ngược lại)

ở đây ta có F[i,j,u] = true nếu tồn tại k sao cho F[i,k,x] = true và F[k+1,j,y] = true và

xy = a; (x , y = ’a’ c);

Để ý thấy ta hoàn toàn có thể tiết kiệm bộ nhớ bằng cách để mảng F kiểu byte để ta dễ dàng truy vết lúc về sau

F[i,j,u] = 0 nếu không thể phân tích được

F[i,j,u] = k nếu F[i,k,x] <> 0 và F[k+1,j,y] <> 0 và xy = u;

Cách tính mảng F ta hoàn toan áp dụng bài toán trên

Ta sẽ trình bày thuật toán như sau:

Const

InputFile = ’GIATRI.INP’ ;

OutputFile = ’GIATRI.OUT’ ;

max = 100 ;

X : array[’a’ ’c’, ’a’ ’c’] of char = ((’b’,’b’,’a’) , (’c’,’b’,’a’) ,(’a’,’c’,’c’));

{X là mảng để lưu phép nhân}

Var

A : string ;

F : array[1 max , 1 max,’a’ ’c’] of byte ;

i , j , n , l , k: integer ;

c , b : char ;

Fi , Fo : text ;

Procedure enter ; { nhập dữ liệu }

begin

Assign(Fi, InputFile) ; Reset(Fi) ;

Assign(Fo , OutputFile) ; Rewrite(Fo ) ;

Readln(Fi , A);

n := length(A) ;

end;

Procedure Init ; { Khởi tạo mảng F }

begin

Fillchar(F , sizeof (F) , 0) ;

For i := 1 to n do

begin

Trang 4

if i < n-1 then

F[i,i+1,X[A[i],A[i+1]]] := i ;

F[i,i,A[i]] := 101 ;

end;

end;

Procedure optimize ; {Ch ương trình quy hoạch động }

begin

for l := 2 to n-1 do

For i := 1 to n-l do

For k := i to i+l-1 do

For b := ’a’ to ’c’ do

if F[i,k,b] <> 0 then

For c := ’a’ to ’c’ do

if F[k+1,i+l,c] <> 0 then

F[i,i+l,X[b,c]] := k ;

end;

Procedure trace(i,j : byte; c : char) ; {dùng đệ quy để truy vết in kết quả } var

k : byte ;

u , v : char ;

begin

if i = j then

begin

write(Fo,A[i]) ;

exit ;

end;

Write(Fo , ’(’) ;

k := F[i,j,c] ;

for u := ’a’ to ’c’ do

For v := ’a’ to ’c’ do

if (F[i,k,u] <> 0) and (F[k+1,j,v] <> 0) and (x[u,v] = c) then

begin

Trace(i,k,u) ; trace(k+1,j,v) ;

end;

Write(Fo , ’)’) ;

end;

{ Ch ương trình chính }

Begin

Enter ;

Init ;

Optimize ;

If F[1,n,’a’] = 0 then write(Fo , 0)

else Trace(1,n,’a’) ;

Close(Fo) ;

Trang 5

End

Ta cũng có thể dùng chương trình đệ quy trên để ghi ra cách nhân trong bài nhân ma trận

Từ các bài toán trên ta rút ra công thúc truy hồi cho các bài có dạng như trên:

F[i,j] := cấu hình tốt nhất (F[i,k] , F[k+1,j]) với i <= k < j

Các bạn có thể tham khảo bài toán sau

Cho n quân bài đặt liền nhau, trên quân bài thứ i có ghi số A[i] Nếu bạn rút quân bài k

thì bạn sẽ nhận số tiền là A[k-1] * A[k] *A[k+1] (1<k<n) Bài toán đặt ra là hãy rút n -2

sao cho số tiền nhận được là lớn nhất

Input

Dòng 1: ghi số n

Dòng thứ 2: ghi n số, số thứ i là số ghi trên quân bài thứ i

Output

Ghi số tiền lớn nhất nhận được

Ngày đăng: 07/09/2012, 10:53

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w