矩阵连乘问题·动态规划

矩阵连乘问题

题目信息

输入

共两行
第一行 N (1<=N<=100),代表矩阵个数。
第二行有N+1 个数,分别为 q(0), q(1), ..., q(n) (1<=q(k)<=2000), 代表第 k 个矩阵是q(k-1)xq(k)维的。

输出

共两行
第一行 M,为最优代价。注:测试用例中 M 值保证小于 2^31
第二行为最优顺序。如 (A1((A2A3)A4)) ,最外层也加括号。
注意:测试用例已经保证了输出结果唯一,所以没有AAA的情况.

测试样例

样例1

6
30 35 15 5 10 20 25
15125
((A1(A2A3))((A4A5)A6))

样例2

1
1 1
0
(A1)

解答

方法一·普通动规

#include <iostream>
#include <climits>

using namespace std;
#define MAX 2010

int ans[MAX][MAX] = {0};//保存矩阵链 A[i..j]的最小代价
int divide[MAX][MAX] = {0};//记录最小代价ans[i,j]对应的分割点

/* 计算n元矩阵链p的最优代价
 * 动态规划的每一步结果储存在 ans与 divide中 */
void MatrixChainOrder(int p[], int n)
{
    //矩阵链长度为2到n
    for (int l = 2; l <= n; l++)
    {    //讨论长度为l的矩阵链A[i..j]
        for (int i = 1; i <= n - l + 1; i++)
        {
            int j = i + l - 1;
            ans[i][j] = INT_MAX;
            //依次讨论每一个分割点d:将矩阵链A[i..j]分成A[i..d]和 A[d+1..j]
            for (int temp, d = i; d < j; d++)
            {
                temp = ans[i][d] + ans[d + 1][j] + p[i - 1] * p[d] * p[j];
                //记录下矩阵链A[i..j]最小的情况
                if (temp < ans[i][j])
                {
                    ans[i][j] = temp;
                    divide[i][j] = d;
                }
            }
        }
    }
}

//根据divide数组,输出A[i..j]的最优情况
void PrintDivide(int i, int j)
{
    if (i == j)
    {
        cout << "A" << i;
    }
    else
    {
        int d = divide[i][j];//分界点
        cout << "(";
        PrintDivide(i, d);//左
        PrintDivide(d + 1, j);//右
        cout << ")";
    }
}

int main()
{
    freopen("E://test.txt", "r", stdin);
    freopen("E://out.txt", "w", stdout);
    int n;
    cin >> n;
    int p[MAX];
    for (int temp, i = 0; i <= n; i++)
        cin >> p[i];

    MatrixChainOrder(p, n);

    cout << ans[1][n] << endl;
    if (n == 1)
    {
        cout << "(A1)";
    }
    else
    {
        PrintDivide(1, n);
    }
    cout << endl;
    return 0;
}

方法二·优化的动规

#include <iostream>
#include <climits>

using namespace std;
#define MAX 2010

int ans[MAX][MAX] = {0}; // 保存矩阵链 A[i..j]的最小代价
int divide[MAX][MAX] = {0};   // 记录最小代价ans[i,j]对应的分割点

/* 计算n元矩阵链p的最优代价
 * 动态规划的每一步结果储存在 ans与 divide中 */
void MatrixChainOrder(int p[], int n)
{
    //初始化divide数组
    for (int i = 1; i <= n; i++)
    {
        divide[i][i] = i;
    }
    //矩阵链长度为2到n
    for (int l = 2; l <= n; l++)
    {
        //讨论长度为l的矩阵链A[i..j]
        for (int i = 1; i <= n - l + 1; i++)
        {
            int j = i + l - 1;
            ans[i][j] = INT_MAX;
            //在特定区间内:依次讨论每一个分割点d,将矩阵链A[i..j]分成A[i..d]和 A[d+1..j]
            for (int temp, d = divide[i][j - 1]; d <= divide[i + 1][j]; d++)
            {
                temp = ans[i][d] + ans[d + 1][j] + p[i - 1] * p[d] * p[j];
                //记录下矩阵链A[i..j]最小的情况
                if (temp < ans[i][j])
                {
                    ans[i][j] = temp;
                    divide[i][j] = d;
                }
            }
        }
    }
}

/* 根据divide数组,输出A[i..j]的最优情况 */
void PrintDivide(int i, int j)
{
    if (i == j)
    {
        cout << "A" << i;
    }
    else
    {
        int d = divide[i][j];//分界点
        cout << "(";
        PrintDivide(i, d);//左
        PrintDivide(d + 1, j);//右
        cout << ")";
    }
}

int main()
{
    freopen("E://test.txt", "r", stdin);
    freopen("E://out.txt", "w", stdout);
    int n;
    cin >> n;
    int p[MAX];
    for (int temp, i = 0; i <= n; i++)
        cin >> p[i];

    MatrixChainOrder(p, n);

    cout << ans[1][n] << endl;
    if (n == 1)
    {
        cout << "(A1)";
    }
    else
    {
        PrintDivide(1, n);
    }
    cout << endl;
    return 0;
}
 

想法

矩阵连乘问题·动态规划
矩阵连乘问题·动态规划

矩阵连乘问题·动态规划
还有老师介绍的另一种方法,自顶而下的一种方法,更加适合于只用计算部分即可的题

矩阵连乘问题·动态规划

1. 对所有i,j, m[i,j]=0
2. LU(1,n)          //LookUp
LU(i,j)
1. 若 m[i,j]>0, 返回 m[i,j]
2. 若 i = = j, 返回0
3.   s[i,j] = i;  m[i,j] = LU(i,i) + LU(i+1,j) + qi-1qiqj; 
4.   对 k = i + 1 到 j-1
5.         t = LU(i, k) + LU(k+1,j) + qi-1qkqj, 
6.         若m[i,j]>t, 则m[i,j]=t; s[i,j]=k;
7.  返回m[i][j]

自底向上与自顶向下的比较

动态规划方法采用自底向上
备忘录方法采用自顶向下
都能解决重叠子问题
当所有子问题都至少要求解一次时,用动态规划方法比较好
当部分子子问题不用求解时,用备忘录方法比较好
矩阵连乘问题宜用动态规划

上一篇:clickhouse算术函数


下一篇:[LeetCode] 1296. Divide Array in Sets of K Consecutive Numbers