zoukankan      html  css  js  c++  java
  • 最优矩阵链乘

    问题描述:

      一个n×m矩阵由n行m列共n×m个数排列而成。两个矩阵A和B可 以相乘当且仅当A的列数等于B的行数。一个n×m的矩阵乘以一个m×p的矩阵等于 一个n×p的矩阵,运算量为m×n×p。矩阵乘法不满足分配律,但满足结合律,因此A×B×C可以按顺序(A×B)×C进行也可以按A×(B×C)来进行。假设A、B、C分别 是2×3,3×4,4×5的,则(A×B)×C的运算量为2×3×4+2×4×5=64,A×(B×C)的运算 量为3×4×5+2×3×5=90。显然第一种顺序节省运算量。给出n个矩阵组成的序列,设计一种方法把它们乘起来,使得总的运算量尽量小。假设第i个矩阵Ai是p[i−1] ×p[i]的。 

    分析:

      可以把这n个矩阵分成两部分,P = Ax Ax A3...Ak和Q = Ak+1 x Ak+2...An,1<= k < n,显然结果就是k循环一遍,取其中PxQ结果最小的。

      dp[i][j]表示从第i个矩阵乘到第j个矩阵所需要的最小代价。

      状态转移方程:dp[i][j] = min(dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j]), i <= k < j

    代码实现:

      写法一:记忆化搜索

    int solve(int i, int j)
    {
        if(dp[i][j] < minn)
            return dp[i][j];
        if(i == j)
            dp[i][j] = 0;
        else
        {
            for(int k = i; k < j; k++)
            {
                int ans = solve(i, k) + solve(k + 1, j) + p[i - 1] * p[k] * p[j];
                if(ans < dp[i][j])
                    dp[i][j] = ans;
            }
        }
        return dp[i][j];
    }

      写法二:递推

    for(int i = 0; i < n; i++)
    {
        //长度为1时
        dp[i][i] = 0;
    }
    for(int len = 2; len <= n; len++)
    {
        //从小到大依次求出相应长度时的情况
        for(int i = 1; i < n - len + 1; i++)
        {
            j = i + len - 1;
            for(k = i; k < j; k++)
            {
                int ans = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j];
                if(ans < dp[i][j])
                    dp[i][j] = ans;
            }
        }
    }

    看一道例题

    题目链接:

      http://poj.org/problem?id=1651

    题意:

      给你n张牌,每次抽一张,每次抽取的价值等于这张牌的值乘以这张牌左右两张的值,其中,第一张和最后一张不能抽,问你怎样的抽取方案能够取到最小价值

    分析:

      典型的最优矩阵链乘,直接看代码理解吧

    代码:

     1 #include<iostream>
     2 using namespace std;
     3 int dp[110][110];    //dp[i][j]表示从i到j中(不包括i和j)抽数能得到的最小值
     4 int num[110];
     5 int main()
     6 {
     7     int n;
     8     cin >> n;
     9     for(int i = 0; i < n; i++)
    10         cin >> num[i];
    11 
    12     for(int i = 0; i < n - 2; i++)
    13         dp[i][i + 2] = num[i] * num[i + 1] * num[i + 2];
    14 
    15     for(int len = 3; len < n; len++)
    16     {
    17         for(int i = 0; i + len < n; i++)
    18         {
    19             int j = i + len;
    20             for(int k = i + 1; k < j; k++)
    21             {
    22                 if(dp[i][j] == 0)
    23                     dp[i][j] = dp[i][k] + dp[k][j] + num[i] * num[k] * num[j];
    24                 else
    25                     dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + num[i] * num[k] * num[j]);
    26             }
    27         }
    28     }
    29     cout << dp[0][n - 1] << endl;
    30     return 0;
    31 }
  • 相关阅读:
    【myEcplise2015】导入喜欢的主题
    【SVN】删除SVN上的历史资源路径和SVN上的历史用户信息
    【Linux】linux命令大全
    【Linux】在虚拟机上安装CentOS7
    Spring MVC+Mybatis 多数据源配置
    Spring 加载类路径外的资源文件
    OkHttp使用详解
    在虚拟机搭建JStrom
    在Windows下搭建RocketMQ
    解决confluence的乱码问题
  • 原文地址:https://www.cnblogs.com/friend-A/p/10293160.html
Copyright © 2011-2022 走看看