题目:
设有矩阵M1,M2,M3,M4,
其维数分别是10×20, 20×50, 50×1 和1×100,现要求出这4个矩阵相乘的结果。我们知道,若矩阵A的维数是p×q,矩阵B的维数是q×r,则A与B相乘后所得矩阵AB的维数是p×r。按照矩阵相乘的定义,求出矩阵AB中的一个元素需要做q次乘法(及q-1次加法)。这样,要计算出AB就需要做p×q×r次乘法。为简单起见,且由于加法比同样数量的乘法所用时间要少得多,故这里我们暂不考虑加法的计算量。由于矩阵连乘满足结合律,故计算矩阵连乘的方式可以有多种。
例如,我们可以按M1(M2(M3M4))的方式去计算,
也可以按(M1(M2M3))M4的方式去计算,所得结果是相同的。
但是值得注意的是,
按前一方式计算需要做125,000次乘法,
而按后一方式计算只需要做2,200次乘法。
由此可见,矩阵连乘的运算次序对于所需要的计算量
(所需乘法次数)有着极大的影响。
M3M4:50*1*100=5,000;M2(M3M4):20*50*100=100,000
M1(M2(M3M4)):10*20*100=20,000
(M2M3):20*50*1=1000;(M1(M2M3)):10*20*1=200 ;
(M1(M2M3))M4:10*1*100=1000
动态规划重在解决状态转移方程,设存储多个矩阵的数组为mat,行为r列为c,动态规划数组为m,m[j][i]表示[j,i]区间上矩阵连乘的最小计算量。则最终结果保存在m[0][n-1]上。状态转移方程为:
m[j][i] = min(m[j][k] + m[k+1][i] + mat[j].r*mat[k].c*mat[i].c)
随着k的值不同,m[j][i]保存最小的值。
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; struct MAT { int r, c; }mat[1005]; int m[1005][1005]; int main(void) { //freopen("9-6.in","r",stdin); int n; scanf("%d",&n);//矩阵个数 for(int i=0; i<n; i++) { scanf("%d%d",&mat[i].r, &mat[i].c); m[i][i] = 0;//每个矩阵的行数和列数 } for(int i=0; i<n; i++) //第i列 { for(int j=i-1; j>=0; j--) //第j行 { int temp = 999999; for(int k=j; k<i; k++) { int x = m[j][k] + m[k+1][i] + mat[j].r*mat[k].c*mat[i].c; temp = min(temp, x); //取不同断点寻找最优子结构 } m[j][i]=temp;//表示[j,i]区间上的最优解 } } printf("%d",m[0][n-1]);//最终结果在m[0][n-1]上 return 0; } //2015-07-31
掌握之后可以去刷POJ 1651
http://poj.org/problem?id=1651
下面是我的Ac代码,这次区间为[i, j]
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; struct mat { int r, c; }m[105]; int a[105], dp[105][105]; int main(void) { //freopen("1651.in","r",stdin); int n; scanf("%d",&n); for(int i=0; i<n; i++) { scanf("%d",&a[i]); dp[i][i] = 0; } for(int i=0; i<n-1; i++) { m[i].r = a[i]; m[i].c = a[i+1]; } n--; for(int i=n-2; i>=0; i--) for(int j=i+1; j<n; j++) { dp[i][j] = 9999999; for(int k=i; k<j; k++) { int x = dp[i][k] + dp[k+1][j] + m[i].r*m[k].c*m[j].c; dp[i][j] = min(dp[i][j], x); } } printf("%d ",dp[0][n-1]); return 0; }