题目
m×\times×n矩阵A与n×\times×p矩阵B相乘需消耗O(mnp)的时间。我们把mnp作为两个矩阵用来相乘所需时间的测量值。
在多个矩阵连乘的情况下,不同矩阵之间的计算顺序不同结果虽然相同,但总共所需要的乘法数量不同。
例子:
A === (256143)\bigl(\begin{matrix} 2 & 5 & 6\\ 1 & 4 & 3 \end{matrix} \bigr)(215463),B === [[264275]]\bigl[
\begin{bmatrix}2 & 6\\4 & 2\\ 7 & 5\end{bmatrix} \bigr][⎣⎡247625⎦⎤],C === (15683321)\bigl(\begin{matrix} 1 & 5 & 6 & 8\\ 3 & 3 & 2 &1\end{matrix} \bigr)(13536281)。计算ABC的乘法次数。
方法一:(AB)C === 2×\times× 3×\times× 2 +++ 2×\times× 2×\times× 4 === 28
方法二:A(BC) === 2×\times× 3×\times× 4 +++ 3×\times× 2×\times× 4 === 48
**注:此处细节略过,学过线性代数的小伙伴应该都知道的。
建立递归关系
我们定义A[i,j](1≤\leq≤ i ≤\leq≤ j ≤\le≤ n)所需要的最少次数为m[i][j],则原问题的最优解是m[1][n]。
状态转移方程为:
m[i][j]={0i=jmini≤k<jm[i][k]+m[k+1][j]+pi−1pkpji<jm[i][j] = \begin{cases} 0 & i = j \\ \min\limits_{i \leq k < j } {m[i][k] + m[k+1][j]+p_{i-1}p_kp_j} & i <j \end{cases}m[i][j]=⎩⎨⎧0i≤k<jminm[i][k]+m[k+1][j]+pi−1pkpji=ji<j
代码
#include<stdio.h>
#define NUM 51
int p[NUM]; //记录矩阵Ai的维数
int m[NUM][NUM];//记录最优值数组
int s[NUM][NUM];//记录最优断开位置的数组s
void MatrixChain(int n) //计算最优值
{
for(int i = 1;i <= n;i++) m[i][i] = 0; //平凡矩阵
for(int r = 2;r <= n;r++) //从第2个对角线到第n个对角线
for(int i = 1; i <= n-r+1;i++) //需要填充的个数
{
int j = i+r-1;//每个填充的纵坐标
m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];//记录初值
s[i][j] = i;//从i处断开
for(int k = i+1; k < j;k++)//判断从i+1到j之间的代价并进行比较
{
int t =m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
if(t < m[i][j]){
m[i][j] = t;
s[i][j] = k;
}
}
}
}
void TraceBack(int i,int j)//构造最优解结构(递归)
{
if(i == j) printf("A%d",i);
else{
printf("(");
TraceBack(i,s[i][j]);
TraceBack(s[i][j]+1,j);
printf(")");
}
}
int main()
{
int n;
scanf("%d",&n);
int i,temp;
for(i = 0;i < n;i++) //输入A1 ~ An-1的行列数
scanf("%d%d",&p[i],&temp);
p[n] = temp;//输入第An个的列数
MatrixChain(n);
printf("%d\n",m[1][n]);//输出最优解
TraceBack(1,n);//构造最优解结构
return 0;
}
填表
都说动态规划其实质就是一个填表的过程,代码的两层填表过程如下图所示:
d= 1 | d = 2 | d = 3 | d = 4 | d = 5 | d = 6 |
---|---|---|---|---|---|
m[1,1] | m[1,2] | m[1,3] | m[1,4] | m[1,5] | m[1,6] |
m[2,2] | m[2,3] | m[2,4] | m[2,5] | m[2,6] | |
m3,3] | m[3,4] | m[3,5] | m[3,6] | ||
m[4,4] | m[4,5] | m[4,6] | |||
m[5,5] | m[5,6] | ||||
m[6,6] |
输入
6
50 10
10 40
40 30
30 5
5 20
20 15
输出
15750
((A1(A2(A3A4)))(A5A6))
总结
怎么讲呢,学到了很多Markdown公式的写法,而矩阵连乘积正是动态规划的一个经典例子。学好动态是算法的一个深化吧!