给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10 * 100,100 * 5和5 * 50,采用(A1A2)A3,乘法次数为10 * 100 * 5 + 10 * 5 * 50=7500次,而采用A1(A2A3),乘法次数为100550+1010050=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
矩阵连乘问题,需要用动态规划来解决,为每个矩阵编号:
输入矩阵:
用p数组来表示矩阵,因为前一个矩阵的列是后一个矩阵的行,所以对于n个矩阵来讲只需输入n+1个数。1号矩阵 = p[0] * p[1] , 2号矩阵 = p[1] * p[2],…
我们假设1号矩阵到3号矩阵已经乘完,那么得到了一个10 * 25 的矩阵(10 * 50 50 * 20 20 * 25得到的最后矩阵为10 * 25 ),假设4号矩阵到5号矩阵也已经乘完,得到了25 * 100 的矩阵,那如果这两个矩阵相乘,需要多少次呢?矩阵的乘法是一行乘一列这样乘的,所以需要10 * 25 * 100 = 25000次乘法运算。10是一号矩阵的行,25是3号矩阵的列,100是5号矩阵的列,我们将矩阵乘法的最少次数保存到m[][]二维数组中,那么m[1][5]就表示1号矩阵乘到5号矩阵需要的次数,拿上面的例子来说,m[1][5]=m[1][3]+m[4][5]+p[0]*p[3]*p[5],意思是说 1号到5号矩阵乘法的次数=1号矩阵乘到3号矩阵的次数 + 4号矩阵乘到5号矩阵的次数 + 这两个乘法的次数(1号矩阵行 * 3号矩阵列 * 5号矩阵行 p[0]*p[3]*p[5]),而对于m[1][3]来说,也是类似于上面的求法,求出最小值。由此可以得出动态转移方程:
当 i = j 时:
m[i][j] = 0
当 i < j 时:
m[i][j] = min{ m[i][k] + m[k+1][j] + pi-1 * pk * pj } , i<=k<j
#include<bits/stdc++.h>
using namespace std;
int p[100];
int m[50][50];
int s[50][50];
void print(int i,int j)
{
if(i == j)
{
printf("A[%d]",i) ;
return ;
}
printf("(");
print(i,s[i][j]);
print(s[i][j]+1,j);
printf(")");
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 0;i<=n;i++)
scanf("%d",&p[i]);
for(int step=2;step<=n;step++) //链长
for(int left=1;left<=n-step+1;left++)
{
int right = left+step-1;
m[left][right] = m[left+1][right]+p[left-1]*p[left]*p[right];
s[left][right] = left;
for(int k=left+1;k<right;k++)
if(m[left][k]+m[k+1][right]+p[left-1]*p[k]*p[right]<m[left][right])
m[left][right] = m[left][k]+m[k+1][right]+p[left-1]*p[k]*p[right],s[left][right] = k;
}
printf("%d\n",m[1][n]);
print(1,n);
}