動態規劃&矩陣連乘
動態規劃的概念
• 與分治方法類似
分-治-合
• 與分治方法不同
子問題之間並非相互獨立
• 基本思想
用一個表記錄所有子問題的解,不管子問題是否被用到,只要它被計算過,就將其結果備份至表中
動態規劃的基本要素
• 最優子結構
利用問題的最優子結構性質,以自底向上的方式遞迴地從子問題的最優解逐步構造出整個問題的最優解。最優子結構是問題能用動態規劃演算法求解的前提。
• 重疊子問題
• 遞迴演算法求解問題時,每次產生的子問題並不總是新問題,有些子問題被反覆計算多次。這種性質稱為子問題的重疊性質。
• 動態規劃演算法,對每一個子問題只解一次,而後將其解儲存在一個表格中,當再次需要解此子問題時,只是簡單地用常數時間檢視一下結果。
• 通常不同的子問題個數隨問題的大小呈多項式增長。因此用動態規劃演算法只需要多項式時間,從而獲得較高的解題效率。
動態規劃的步驟
• 找出最優解的性質
• 遞迴地定義最優值
• 以自底向上的方式計算出最優值
• 根據計算獲得的最優值,構造最優解
矩陣連乘
• 問題
給定n個矩陣(A1,A2,…,An),其中Ai和Ai+1是可乘的,求解這n個矩陣的連乘積A?
• 矩陣連乘積A=A1A2A3A4
• 計算次序
(((A1A2)A3)A4))
((A1(A2A3))A4)
(A1((A2A3)A4))
(A1(A2(A3A4)))
((A1A2)(A3A4))
• 最優子結構性質
原問題的最優解包含其子問題的最優解
A[1:n]的最優計算次序所包含的計算矩陣子鏈A[1:k]和A[k+1:n]也是最優的
數學歸納法可以證明
• 建立遞迴關係
令A[i:j]所需的最少數乘次數為m[i][j].
當i=j時,m[i][j]=0
當i<j時,m[i][j]=m[i][k]+m[k+1][j] +pi-1*pk*pj
構造遞迴關係
將對應的m[i][j]的斷開位置k記為s[i][j],可遞迴的構造最優解
• 遞迴求解
1 int matrixChainRecur(int i, int j){ 2 if (i == j) return 0; 3 int u = matrixChainRecur(i, i) + matrixChainRecur(i+1, j) 4 + p[i-1] * p[i] * p[j]; 5 s[i][j] = i; 6 for (int k = i + 1; k < j; k++) { 7 int t = matrixChainRecur(i, k) + matrixChainRecur(k+1, j) 8 + p[i-1] * p[k] * p[j]; 9 if (t < u){ 10 u = t; 11 s[i][j] = k; 12 } 13 } 14 return u; 15 }
• 在遞迴計算時,許多子問題被重複計算多次。這也是該問題可用動態規劃演算法求解的又一顯著特徵。
• 用動態規劃演算法解此問題,可依據其遞迴式以自底向上的方式進行計算。在計算過程中,儲存已解決的子問題答案。每個子問題只計算一次,而在後面需要時只要簡單查一下,從而避免大量的重複計算,最終得到多項式時間的演算法
• 計算最優值
A1 |
A2 |
A3 |
A4 |
A5 |
A6 |
30´35 |
35´15 |
15´5 |
5´10 |
10´20 |
20´25 |
1 # include<stdio.h> 2 # include<iostream> 3 using namespace std; 4 5 int m[20][20],s[20][20],n,p[20]; 6 void matrixChain() 7 { 8 int i,j,k,r; 9 for(i=1; i<=n; i++) m[i][i]=0; 10 for(r=2; r<=n; r++) //對角線 11 for( i=1; i<=n-r+1; i++) //行 12 { 13 j = r+i-1; //列 14 m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j]; 15 s[i][j]=i; 16 for(k = i+1; k<j; k++) 17 { 18 int temp=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]; 19 if(temp<m[i][j]) 20 { 21 m[i][j]=temp; 22 s[i][j]=k; 23 } 24 } 25 } 26 } 27 29 void traceback(int i,int j) 30 { 31 if(i==j)return ; 32 traceback(i,s[i][j]); 33 traceback(s[i][j]+1,j); 34 cout<<"Multiply A"<<i<<","<<s[i][j]<<"and A"<<s[i][j]+1<<","<<j<<endl; 35 } 36 38 int main() 39 { 40 cin>>n; 41 for(int i=0; i<=n; i++) 42 cin>>p[i]; 43 //測試資料可以設為六個矩陣分別為 44 //A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25] 45 //則p[0-6]={30,35,15,5,10,20,25} 46 //輸入:30 35 15 5 10 20 25 47 matrixChain(); 48 traceback(1,n); 49 //最終解值為m[1][n]; 50 cout<<m[1][n]<<endl; 51 return 0; 52 }