問題描述
給你一個數組,第一個元素為第一個矩陣的行數,末尾元素為最后一個矩陣的列數,中間元素為前一個矩陣的列數和后一個舉證的行數。現在要將這些矩陣相乘,要求你求出最少需要做多少次乘法才能得到結果。(矩陣的乘法滿足結合律)。
例如,對於輸入的一個數組 {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]; } }