动态规划问题(八)最少矩阵乘法次数
问题描述
给你一个数组,第一个元素为第一个矩阵的行数,末尾元素为最后一个矩阵的列数,中间元素为前一个矩阵的列数和后一个举证的行数。现在要将这些矩阵相乘,要求你求出最少需要做多少次乘法才能得到结果。(矩阵的乘法满足结合律)。
例如,对于输入的一个数组 {40, 20, 30, 10, 30},表示输入的矩阵为 \(A(10, 20)、B(20, 30)、C(30, 10)、D(10, 30)\),最少需要做 26000 次乘法才能得到结果,即 (A(BC))D ——> 20*30*10 + 40*20*10 + 40*10*30 = 26000
。
解决思路
解决该问题的方式很简单,将所有可能的结合方式一一列举出来,计算结果得到最小值即可。
- 递归
- 由于第一个元素的特殊性,因此需要特别处理一下
- 当只有一个元素时,无需做任何乘法操作
- 动态规划
- 这是一个存在大量重复计算的问题,因此使用动态规划来处理可以明显地提高效率
实现
-
递归
public class Solution { /** * @param values: 矩阵地表示数组 * @param i: 计算地开始位置,由于要处理第一个元素地特殊性,因此需要从第二个矩阵开始计算 * @param j: 末尾元素位置索引 */ public static int minMultiMatrixRecur(int[] values, int i, int j) { if (i == j) return 0; int ans = Integer.MAX_VALUE; for (int k = i; k < j; ++k) { ans = Math.min( ans, minMultiMatrixRecur(values, i, k) // 这里计算的是结合的 i -> k 个矩阵 + minMultiMatrixRecur(values, k + 1, j) // 这里结合的是 k + 1 -> j 个矩阵 + values[i - 1] * values[k] * values[j] // 通过 k 将矩阵分为两部分,但是无论如何,最后这两个矩阵也是要相乘的,这里表示的就是最后的计算结果 ); } return ans; } }
-
动态规划
public class Solution { public static int minMultiMatrix(int[] values, int n) { int[][] dp = new int[n][n]; // 截取的步长,这是为了记录重复计算的中间结果 for (int L = 2; L < n; ++L) { for (int i = 1; i < n - L + 1; ++i) { int j = i + L - 1; // dp[i][j] = Integer.MAX_VALUE; // 从 i 到 j 的最少乘法次数,注意,这里的 i 代表的是第二个元素而不是第一个 for (int k = i; k < j; ++k) dp[i][j] = Math.min( dp[i][j], dp[i][k] + dp[k + 1][j] + values[i - 1] * values[k] * values[j] // 与上文递归的方式相对应 ); } } return dp[1][n - 1]; } }