动态规划之矩阵连乘和POJ 1651

题目:

设有矩阵M1,M2,M3,M4,
其维数分别是10×20, 20×50, 50×1 和100,现要求出这4个矩阵相乘的结果。我们知道,若矩阵A的维数是q,矩阵B的维数是r,则A与B相乘后所得矩阵AB的维数是r。按照矩阵相乘的定义,求出矩阵AB中的一个元素需要做q次乘法(及q-1次加法)。这样,要计算出AB就需要做r次乘法。为简单起见,且由于加法比同样数量的乘法所用时间要少得多,故这里我们暂不考虑加法的计算量。由于矩阵连乘满足结合律,故计算矩阵连乘的方式可以有多种。

 

例如,我们可以按M1(M2(M3M4))的方式去计算,

也可以按(M1(M2M3))M4的方式去计算,所得结果是相同的。

但是值得注意的是,

按前一方式计算需要做125,000次乘法,

而按后一方式计算只需要做2,200次乘法。

由此可见,矩阵连乘的运算次序对于所需要的计算量

(所需乘法次数)有着极大的影响。

M3M4:50*1*100=5,000;M2(M3M4):20*50*100=100,000

M1(M2(M3M4)):10*20*100=20,000

(M2M3):20*50*1=1000;(M1(M2M3)):10*20*1=200 ;

(M1(M2M3))M4:10*1*100=1000

 

动态规划重在解决状态转移方程,设存储多个矩阵的数组为mat,行为r列为c,动态规划数组为m,m[j][i]表示[j,i]区间上矩阵连乘的最小计算量。则最终结果保存在m[0][n-1]上。状态转移方程为:

m[j][i] = min(m[j][k] + m[k+1][i] + mat[j].r*mat[k].c*mat[i].c)
随着k的值不同,m[j][i]保存最小的值。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct MAT
{
  int r, c;
}mat[1005];
int m[1005][1005];
int main(void)
{
    //freopen("9-6.in","r",stdin);
  int n;
  scanf("%d",&n);//矩阵个数
  for(int i=0; i<n; i++)
  {
    scanf("%d%d",&mat[i].r, &mat[i].c);
    m[i][i] = 0;//每个矩阵的行数和列数
  }
  for(int i=0; i<n; i++)  //第i列
  {
    for(int j=i-1; j>=0; j--) //第j行
    {
       int temp = 999999;
       for(int k=j; k<i; k++)
       {
            int x = m[j][k] + m[k+1][i] + mat[j].r*mat[k].c*mat[i].c;
         temp = min(temp, x);    //取不同断点寻找最优子结构
       }
       m[j][i]=temp;//表示[j,i]区间上的最优解
    }
  }
  printf("%d",m[0][n-1]);//最终结果在m[0][n-1]上
    return 0;
}
//2015-07-31

 掌握之后可以去刷POJ 1651

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

下面是我的Ac代码,这次区间为[i, j]

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct mat
{
  int r, c;
}m[105];
int a[105], dp[105][105];
int main(void)
{
    //freopen("1651.in","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
  {
    scanf("%d",&a[i]);
    dp[i][i] = 0;
  }
  for(int i=0; i<n-1; i++)
  {
    m[i].r = a[i];
    m[i].c = a[i+1];
  }
  n--;
  for(int i=n-2; i>=0; i--)
    for(int j=i+1; j<n; j++)
    {
        dp[i][j] = 9999999; 
      for(int k=i; k<j; k++)
      {
        int x = dp[i][k] + dp[k+1][j] + m[i].r*m[k].c*m[j].c;
        dp[i][j] = min(dp[i][j], x);
      }
    }
  printf("%d\n",dp[0][n-1]);
    return 0;
}

 

转载于:https://www.cnblogs.com/mycd/p/4693977.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值