• 矩阵【DP】【记搜】


    题目大意:
    N个矩阵相乘,求进行乘法的最少次数,我们认为两个矩阵A(m×n)×B(n×p)的乘法次数为m×n×p次。

    Input

    3
    50 10
    10 20
    20 5

    Output

    3500

    思路:
    考试时这道题是第一题,推了半天没搞懂,样例也看不懂。总以为正确答案是50×10×20×5=50000
    但是并不是这样的。
    我们设第2n+1行的输入数据为a[i]b[i],那么我们必须先证明b[i]=a[i+1]

    证明:
    根据矩阵乘法的性质,矩阵A*矩阵B 不等于 矩阵B*矩阵A,那么我们能确定,A*B*C不等于A*C*B,
    那么题目又说输入确保能够相乘,所以b[i]=a[i+1]。
    证毕。

    那么再根据A(m×n)×B(n×p)=C(m×p),所以我们可以利用矩阵乘法可以用乘法结合律的特点,将A(m×n)×B(n×p)×C(m×p)变成(A(m×n)×B(n×p))×C(m×p),两两相乘,就可以求出正确答案。

    那么如何求最小值呢?

    有两种方法求最小值,分别指区间DP和记搜,这里就介绍DP的方法。

    可以用f[i][j]表示矩阵i到矩阵j都被合并的最小值,再从ij中枚举k,将矩阵分为两边(类似Floyd思想,ij最短路就枚举其他点kf[i][j]=min(f[i][j],f[i][k]+f[k][j])),再加上题目给出的A(m×n)×B(n×p)要乘m×n×p次,即a[i]×a[k+1]×b[j]a[k+1]=b[k],已经证过,所以也可以是a[i]×b[k]×b[j])。完整方程如下:

    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+a[i]*a[k+1]*b[j]);

    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+a[i]*b[k]*b[j]);

    那么ijk分别该怎么枚举呢?

    不难想到,由于是矩阵i到矩阵j,所以必然有ij,所以j就要从2枚举到n。(1不能枚举,因为如果枚举了1,i0,明显不成立)。那么根据ij,也可以推出i要从j1枚举到1。这里必须要倒着枚举,如果从1开始枚举的话,a[i][j]里面没有任何一个区间求出最小值,就无法转移。那么k就很简单了,如上文所述,有ikj,所以k自然就在ij中枚举了。

    那么还有最后一个问题:怎么初始化?

    由于要求最小值,所以很容易想到要讲f数组全部赋值为INF,但是。。。

    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+a[i]*a[k+1]*b[j]);

    m=a[i]×a[k+1]×b[j],再带入方程,发现。。。

    f[i][j]=min(INF,INF+INF+m)

    f[i][j]=min(INF,INF)

    f[i][j]=INF

    怎么回事?

    再仔细想一想,f[i][i]这个矩阵的值不可能是INF,因为它是它本身,所以f[i][i]=0

    那么当i=j1k=i时,

    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+a[i]*a[k+1]*b[j]);

    f[i][j]=min(INF,f[i][i]+f[i+1][j]+m);

    f[i][j]=min(INF,f[i][i]+f[j][j]+m);

    f[i][j]=min(INF,0+0+m);

    f[i][j]=min(INF,m);

    f[i][j]=m;

    所以答案也出来了:从矩阵1乘到矩阵n,即f[1][n]


    代码:

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    const int inf=99999999;
    int n,a[1001],b[1001],f[601][601];
    
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
         scanf("%d%d",&a[i],&b[i]);
        for (int i=1;i<=600;i++)
         for (int j=1;j<=600;j++)  //初始化
          if (i!=j) f[i][j]=inf;
        for (int j=2;j<=n;j++)
         for (int i=j-1;i>=1;i--)
          for (int k=i;k<j;k++)
           f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+a[i]*a[k+1]*b[j]);  //方程不解释
        printf("%d\n",f[1][n]);
        return 0;
    }
  • 相关阅读:
    Python:Lasso方法、GM预测模型、神经网络预测模型之财政收入影响因素分析及预测
    ARIMA模型构建、预测——基于Python
    家用电器用户行为分析与事件识别学习笔记
    JQData数据提取及MySQL简单操作——基于Python
    android 沉浸通知栏
    PullToRefreshScrollView 修改下拉刷新图标
    Android AlertDialog 设置setSingleChoiceItems不显示列表的原因【setMessage和setSingleChoiceItems不能同时使用】
    图片跑马灯抽奖,本地图片变换简单实现
    android知识点大总结
    Android 面试精华题目总结
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/9313050.html
Copyright © 2020-2023  润新知