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

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

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

Thông tin tài liệu

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

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, 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 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 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] 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; 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 chẳng hạn ta có aa = b ; ac = c ; chú ý ở đây không có tính kết hợp 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 F[k+1,j,y] = true 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 F[k+1,j,y] <> 0 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 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) ; 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 . 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. 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

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