动态规划经典题目-矩阵链乘法

一、题目描述

​ 给定n个矩阵{A1,A2,A3,…,An},其中,Ai和Ai+1(i=1,2,…,n-1)是可乘的。用括号的方法表示矩阵连乘的次序,不同的计算次序计算量(乘法次数)是不同,找出一种加括号的方法,使得矩阵连乘的计算量最小。

​ 设两个矩阵Mixj、Mjxp相乘运算次数则为i x j x p。

示例:

​ A1是M5x10的矩阵;

​ A2是M5x100的矩阵;

​ A3是M100x2的矩阵;

​ 那么有两种加括号的方法:

​ (1) (A1A2)A3;

​ (2) A1(A2A3);

​ 第一种加括号方法运算量:5 x 10 x100 + 5 x 100 x 2 = 6000;

​ 第二种加括号方法运算量:10x 100 x2 + 5 x 10 x 2 = 2100;

二、解题思路

​ 设输入矩阵如下表格:

矩阵A1A2A3A4A5
规模3 x 55 x 1010 x 88 x 22 x 4

​ 转化为数组P表示为:

P[0]P[1]P[2]P[3]P[4]P[5]
3510824

所以P[0]和P[1]表示A1,P[1]和[2]表示A2以此类推即可。

1. 定义状态

​ 设dp[i][j]表示Ai到Aj所需要最小计算量,i和j从1开始计数,那么我们最终要求出的是dp[1][P.length -1]即为矩阵A1到An的最小计算量,其中n = P.length -1;

​ 我么可以对dp[i][j]进行拆分,如果i和j满足j - i >= 1时,则i和j中间必有一点k,即k表示矩阵Ak。可以从Ak进行拆分两个序列(Ai到Ak)和(Ak+1到Aj),两个序列乘法最少的计算量分别为dp[i][k]和dp[k+1][j]。两个子序列矩阵合并后计算量为P[i-1] x P[K] x P[j]。所以dp[i][j] = dp[i][k] + dp[k+1][j] + P[i-1] x P[K] x P[j],当然位置k可能有多个,我们取最小花费那个。

2. 定义状态转移方程

当j - i <= 0时,有

d p [ i ] [ j ] = 0 dp[i][j] = 0 dp[i][j]=0

否则

d p [ i ] [ j ] = m a x ( d p [ i ] [ k ] + d p [ k ] [ j ] + P [ i − 1 ] × P [ k ] × P [ j ] ) , i ≤ k < j dp[i][j] = max( dp[i][k] + dp[k][j] + P[i-1] \times P[k] \times P[j]), i \leq k < j dp[i][j]=max(dp[i][k]+dp[k][j]+P[i1]×P[k]×P[j]),ik<j

3. 初始化

​ 当j - i <= 0时,有 d p [ i ] [ j ] = 0 dp[i][j] = 0 dp[i][j]=0

4. 计算方式

​ 自底向上,自左向右计算。这里解释下是指dp二维表自底向上是指i从大往小计算,自左向右计算是指j从小到大计算。

三、代码实现

/**
 * 最小矩阵乘法运算数量
 *
 * @author hh
 * @date 2021-5-18 23:32
 */
public class MinMatrixChain {

    public int minCalcCount(int[] matrix, int[][] trace) {
        if (matrix.length < 3) {
            throw new IllegalArgumentException("非法参数");
        }
        int[][] dp = new int[matrix.length][matrix.length];
        for (int i = matrix.length - 1; i >= 1; i--) {
            for (int j = 1; j <= matrix.length-1; j++) {
                if (j - i == 0) {
                    dp[i][j] = 0;
                    continue;
                }
                dp[i][j] = Integer.MAX_VALUE;
                for (int k = i; k < j; k++) {
                    int temp = dp[i][k] + dp[k+1][j] + matrix[i - 1] * matrix[k] * matrix[j];
                    if (temp < dp[i][j]) {
                        dp[i][j] = temp;
                        trace[i][j] = k;
                    }
                }
            }
        }
        return dp[1][matrix.length-1];
    }

    public void print(int[] matrix, int[][] trace, int startIndex, int endIndex) {
        if (endIndex - startIndex <= 0) {
            System.out.print("A" + startIndex + " ");
        } else {
            System.out.print("(");
            print(matrix, trace, startIndex, trace[startIndex][endIndex]);
            print(matrix, trace, trace[startIndex][endIndex] + 1, endIndex);
            System.out.print(")");
        }
    }

    public static void main(String[] args) {
        int[] matrix = new int[]{3, 5, 10, 8, 2, 4};
        int[][] trace = new int[matrix.length][matrix.length];
        MinMatrixChain minMatrixChain = new MinMatrixChain();
        System.out.println(minMatrixChain.minCalcCount(matrix, trace));
        minMatrixChain.print(matrix, trace, 1, matrix.length-1);
    }
}

四、执行结果

在这里插入图片描述

五、思考

​ 本题和字符串切分的做法非常相似,都是对dp数组进行线性划分,读者有时间可以看我的另一篇文章动态规划经典题目-字符串切分,进行举一反三。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
,请问您能否介绍一下这个问题以及它的解题思路? 当然可以,动态规划经典题目 c 是一个经典动态规划问题,通常称为 01 背包问题。其基本思想是将一组物品装入一个背包中,每个物品对应一个重量和一个价值,在限定的总重量内,选择一些物品装入背包中,使得装入的物品总价值最大。 该问题的一般形式是:给定一个背包容量 C,和 N 个物品,其中第 i 个物品的重量是 w[i],价值是 v[i]。求解将哪些物品装入背包可使得这些物品的重量总和不超过背包容量,且价值总和最大。 该问题可以使用动态规划求解,动态规划的一般思路是将问题划分成若干子问题,通过寻找子问题之间的转移关系进行求解。对于 01 背包问题,我们可以定义一个二维数组 dp[i][j],表示将前 i 个物品放入容量为 j 的背包中所能获得的最大价值。则状态转移方程为: dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]) 其中 dp[i-1][j] 表示不选第 i 个物品,dp[i-1][j-w[i]]+v[i] 表示选第 i 个物品。这个方程的含义是,在只考虑前 i 个物品的情况下,如果容量小于第 i 个物品的重量,那么第 i 个物品不能选;否则,我们需要决策选还是不选第 i 个物品,选了第 i 个物品后,背包容量就减少了 w[i],同时价值增加了 v[i]。 最后的答案就是 dp[N][C],也就是将所有物品都考虑完之后,在背包容量为 C 的情况下所能获得的最大价值。 希望这个回答能够帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值