動態規劃——矩陣連乘的問題


《問題的引出》

看下面一個例子,計算三個矩陣連乘{A1,A2,A3};維數分別爲10*100 , 100*5 , 5*50

按此順序計算需要的次數((A1*A2)*A3):10X100X5+10X5X50=7500次

按此順序計算需要的次數(A1*(A2*A3)):10X5X50+10X100X50=75000次

所以問題是:如何確定運算順序,可以使計算量達到最小化。

枚舉顯然不可,如果枚舉的話,相當於一個“完全加括號問題”,次數爲卡特蘭數,卡特蘭數指數增長,必然不行。

《建立遞歸關係》

子問題狀態的建模(很關鍵):令m[i][j]表示第i個矩陣至第j個矩陣這段的最優解。

顯然如果i=j,則m[i][j]這段中就一個矩陣,需要計算的次數爲0;

     如果i>j,則m[i][j]=min{m[i][k]+m[k+1][j]+p[i-1]Xp[k]Xp[j]},其中k,在i與j之間遊蕩,所以i<=k<j ;

代碼實現時需要注意的問題:計算順序!!!

因爲你要保證在計算m[i][j]查找m[i][k]和m[k+1][j]的時候,m[i][k]和m[k+1][j]已經計算出來了。

觀察座標的關係如圖:

所以計算順序如上右圖:相應的計算順序對應代碼爲13-15行

m[1][n]即爲最終求解,最終的輸出想爲((A1(A2 A3))((A4 A5)A6))的形式,不過沒有成功,待思考...

複製代碼
1 #include<iostream> 2  using namespace std; 3  const int MAX = 100; 4 //p用來記錄矩陣的行列,main函數中有說明 5 //m[i][j]用來記錄第i個矩陣至第j個矩陣的最優解 6 //s[][]用來記錄從哪裏斷開的纔可得到該最優解 7 int p[MAX+1],m[MAX][MAX],s[MAX][MAX]; 8 int n;//矩陣個數 9 10 void matrixChain(){ 11 for(int i=1;i<=n;i++)m[i][i]=0; 12 13 for(int r=2;r<=n;r++)//對角線循環 14 for(int i=1;i<=n-r+1;i++){//行循環 15 int j = r+i-1;//列的控制 16 //找m[i][j]的最小值,先初始化一下,令k=i 17 m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j]; 18 s[i][j]=i; 19 //k從i+1到j-1循環找m[i][j]的最小值 20 for(int k = i+1;k<j;k++){ 21 int temp=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]; 22 if(temp<m[i][j]){ 23 m[i][j]=temp; 24 //s[][]用來記錄在子序列i-j段中,在k位置處 25 //斷開能得到最優解 26 s[i][j]=k; 27 } 28 } 29 } 30 } 31 32 //根據s[][]記錄的各個子段的最優解,將其輸出 33 void traceback(int i,int j){ 34 if(i==j)return ; 35 36 traceback(i,s[i][j]); 37 traceback(s[i][j]+1,j); 38 cout<<"Multiply A"<<i<<","<<s[i][j]<<"and A"<<s[i][j]+1<<","<<j<<endl; 39 } 40 41 int main(){ 42 cin>>n; 43 for(int i=0;i<=n;i++)cin>>p[i]; 44 //測試數據可以設爲六個矩陣分別爲 45 //A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25] 46 //則p[0-6]={30,35,15,5,10,20,25} 47 //輸入:6 30 35 15 5 10 20 25 48 matrixChain(); 49 50 traceback(1,n); 51 //最終解值爲m[1][n]; 52 cout<<m[1][n]<<endl; 53 return 0; 54 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章