题目
m
×
\times
×n矩阵A与n
×
\times
×p矩阵B相乘需消耗O(mnp)的时间。我们把mnp作为两个矩阵用来相乘所需时间的测量值。
在多个矩阵连乘的情况下,不同矩阵之间的计算顺序不同结果虽然相同,但总共所需要的乘法数量不同。
例子:
A
=
=
=
(
2
5
6
1
4
3
)
\bigl(\begin{matrix} 2 & 5 & 6\\ 1 & 4 & 3 \end{matrix} \bigr)
(215463),B
=
=
=
[
[
2
6
4
2
7
5
]
]
\bigl[ \begin{bmatrix}2 & 6\\4 & 2\\ 7 & 5\end{bmatrix} \bigr]
[⎣⎡247625⎦⎤],C
=
=
=
(
1
5
6
8
3
3
2
1
)
\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 ] = { 0 i = j min i ≤ k < j m [ i ] [ k ] + m [ k + 1 ] [ j ] + p i − 1 p k p j i < j m[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公式的写法,而矩阵连乘积正是动态规划的一个经典例子。学好动态是算法的一个深化吧!