【问题描述】给定n个矩阵M1,M2...MnM_1,M_2...M_nM1,M2...Mn,他们的维数分别是r1∗c1,r2∗c2...rn∗cnr_1*c_1,r_2*c_2...r_n*c_nr1∗c1,r2∗c2...rn∗cn,要求使用【动态规划】的策略求解矩阵连乘的最优计算代价(总乘法次数最少)。题目保证矩阵相乘一定是有效的。
例如有三个矩阵M1,M2,M3M_1,M_2,M_3M1,M2,M3,他们的维度分别是2∗10,10∗2,2∗102*10,10*2,2*102∗10,10∗2,2∗10。按照矩阵乘法的结合律,可以先把M1M_1M1和M2M_2M2相乘,然后把结果和M3M_3M3相乘,总的乘法次数为2∗10∗2+2∗2∗10=802*10*2+2*2*10=802∗10∗2+2∗2∗10=80次;也可以先把M2M_2M2和M3M_3M3相乘,再用M1M_1M1去相乘,这种方式下总的乘法次数为10∗2∗10+2∗10∗10=40010*2*10+2*10*10=40010∗2∗10+2∗10∗10=400次。因此最优计算代价为80。
【输入形式】输入的第1行中有1个数字nnn,表示矩阵的个数;接下来nnn行,每行2个整数rir_iri和cic_ici,分别表示矩阵MiM_iMi的行数和列数。
【输出形式】输出1行中有一个数字,表示nnn个矩阵相乘的最优计算代价。
【样例输入】
3
2 10
10 2
2 10
【样例输出】
80
【说明】
n>=2n>=2n>=2
1<=ri,ci<=201<=r_i,c_i<=201<=ri,ci<=20
我们定义dp[i][j]dp[i][j]dp[i][j]为从第iii个矩阵连乘到第jjj个矩阵的最优代价,那么我们可以得到状态转移方程:
∀k∈[i,j),dp[i][j]=dp[i][k]+dp[k+1][j]+r[i]∗c[k]∗c[j]\forall k \in [i,j),dp[i][j]=dp[i][k]+dp[k+1][j]+r[i]*c[k]*c[j]∀k∈[i,j),dp[i][j]=dp[i][k]+dp[k+1][j]+r[i]∗c[k]∗c[j]
解释一下,这个方程的意思就是从第iii号矩阵到第jjj号矩阵的连乘的最优代价等于第iii个矩阵到第kkk个矩阵的最优代价加上第k+1k+1k+1到第jjj个矩阵的最优代价再加上这两边矩阵相乘的代价,其中kkk是大于等于iii且小于jjj的一个正整数。
更通俗地来说,第kkk个矩阵作为第iii号矩阵到第jjj号矩阵的一个分割线,把整条矩阵链分成了两部分,那么总的最优代价就等于左边部分的最优代价加上右边部分的最优代价,再加上把左右两部分拼回去的代价。

相当于说,我分割好后,先把左边的矩阵链按照最优的方法计算完得到一个矩阵A,并把代价记下来,再把右边的矩阵链也按照最优的方法计算完得到另一个矩阵B,也把代价记下来,最终,将A与B相乘,这也会产生代价,最后把这三个代价相加,就得到整条矩阵链计算的代价。
但是,不同的kkk会得到不同的代价,因此要在k的取值范围内枚举并计算将每一个kkk作为分割线时,所产生的代价是多少,并取最小值作为整条矩阵链的最优代价。
说了这么多,可能还需要补充一下矩阵相乘的代价需要怎样计算,举例来说,一个1×31\times31×3的矩阵和一个3×53\times53×5的矩阵相乘所产生的计算代价就是1×3×51\times3\times51×3×5;一个1×51\times51×5的矩阵和一个5×55\times55×5的矩阵相乘所产生的计算代价就是1×5×51\times5\times51×5×5。
根据以上举例,可以总结出规律:
一个n1×m1n_1 \times m_1n1×m1的矩阵和一个n2×m2n_2 \times m_2n2×m2的矩阵相乘所产生的计算代价就是n1×m1×m2n_1 \times m_1 \times m_2n1×m1×m2,当然,众所周知,两个矩阵相乘的必要条件是m1=n2m_1=n_2m1=n2,所以把m1m_1m1替换成n2n_2n2也是ok的。
其实这个代价仅代表的是两个矩阵相乘所需要使用乘法的次数,我们知道,矩阵相乘不仅需要乘法,也需要加法,但如果单单只是想要比较计算代价来得出最优方法,省略掉加法代价也无伤大雅。
另外,对于一个矩阵链M1×M2×M3×...×MnM_1\times M_2 \times M_3\times...\times M_nM1×M2×M3×...×Mn来说,它的答案矩阵的规模一定是r1×cnr_1\times c_nr1×cn
明确了状态转移方程的意义之后,便需要知道初始状态的情形
因此,我们dp的路线也有所改变,具体做法代码中已给出,外层循环枚举每一条对角线ddd,第二层循环枚举行号iii,根据行号iii和对角线号ddd可以得出列号t=i+d−1t=i+d-1t=i+d−1,最后一层循环枚举kkk。

#include
using namespace std;
const int inf = 0x3f3f3f3f;
int n, r[21], c[21], dp[21][21];
//def:dp[i][j]代表从第i个矩阵到第j个矩阵的连乘的最优代价void init() {for (int i = 1; i <= n; i++) {dp[i][i] = 0;}
}int main()
{cin >> n;for (int i = 1; i <= n; i++) {cin >> r[i] >> c[i];}init();for (int d = 2; d <= n; d++) {for (int i = 1; i <= n - d + 1; i++) {int t = i + d - 1; dp[i][t] = inf;for (int k = i; k < t; k++) {int cost = dp[i][k] + dp[k + 1][t] + r[i] * c[k] * c[t];if (cost < dp[i][t]) {dp[i][t] = cost;}}}}cout << dp[1][n];
}
上一篇:企业信息化的供给侧改革