矩阵连乘问题(动态规划)

来源:互联网 发布:水利工程用什么软件 编辑:程序博客网 时间:2024/05/17 09:02

计算三个矩阵连乘{A1,A2,A3},维数分别为10*100 , 100*5 , 5*50

按此顺序计算需要的次数((A1*A2)*A3):10X100X5+10X5X50=7500次

按此顺序计算需要的次数(A1*(A2*A3)):10X5X50+10X100X50=75000次

所以问题是:如何确定运算顺序,可以使计算量达到最小化。枚举显然不可,如果枚举的话,相当于一个“完全加括号问题”,次数为卡特兰数,卡特兰数指数增长,必然不行。

子问题状态的建模(很关键):

将AiAi+1...Aj简记为A[i:j],(0<=i<=j<=n-1),所需的最少数乘次数为m[i][j],则原问题的最优值为m[0][n-1] 

显然如果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 ;

辅助数组s来记录计算m[i][j]时取得最优代价处的k的值。

m和s数组的计算顺序如图所示(只用到上三角矩阵):


#include<iostream>#include<iomanip>#define SIZE 6 //控制参与连乘矩阵的个数using namespace std;/** 数组m记录最少数乘次数,s记录断开的位置k * AiAi+1...Aj简记为A[i:j],(0<=i<=j<=n-1),所需的最少数乘次数为m[i][j],则原问题的最优值为m[0][n-1] */int m[SIZE][SIZE],s[SIZE][SIZE];void MatrixChain(int p[],int n){for(int i=0;i<n;i++)//i=j时为单一矩阵,即矩阵链长度为1,无需计算m[i][i]=0;    for(int l=2;l<=n;l++)//l为矩阵链长度,从2开始循环     {for(int i=0;i<n-l+1;i++)//l长度的矩阵链的第一个矩阵, 即A[i:j]中的i {int j=i+l-1;//根据当前的长度算出矩阵链的最后一个矩阵m[i][j]=m[i+1][j]+p[i]*p[i+1]*p[j+1];s[i][j]=i+1;for(int k=i+1;k<j;k++)//遍历所有断开位置k{int min=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];//寻找最小值 if(min<m[i][j]) {m[i][j]=min;s[i][j]=k+1;}}}    }}void Traceback(int i,int j) //找出s数组中记录的最优断开点{if(i==j)cout<<"A"<<i+1;else{cout<<"(";Traceback(i,s[i][j]-1);Traceback(s[i][j],j);cout<<")";}}int main(){system("title 矩阵连乘问题");int p[SIZE+1]={30,35,15,5,10,20,25}; //p记录矩阵的行列数MatrixChain(p,SIZE);cout<<"输出m矩阵为:"<<endl;for(int i=0;i<SIZE;i++){for(int j=0;j<SIZE;j++){cout<<setw(5)<<m[i][j]<<" ";}cout<<endl;}cout<<endl<<endl;cout<<"输出s矩阵为:"<<endl;for(int i=0;i<SIZE;i++){for(int j=0;j<SIZE;j++){cout<<setw(5)<<s[i][j]<<" ";}cout<<endl;}cout<<"\n矩阵连乘加括号方式为:";Traceback(0,SIZE-1);return 0;}


参考 http://www.cnblogs.com/liushang0419/archive/2011/04/27/2030970.html

参考《算法导论》 P197-201