首页 > 技术文章 > DP:矩阵连乘问题(动态规划)

ljh354114513 2021-04-12 20:45 原文

DP 矩阵连乘问题

动态规划

  1. 找出最优解的性质,刻画其结构特征。
  2. 递归地定义最优值。
  3. 以自底向上的方式计算最优值。
  4. 根据计算最优值得到的信息构造最优解。

矩阵链乘法问题可表述如下:给定n个矩阵构成的一个链<A1,A2,A3……An>,其中i=1,2,……n,矩阵Ai维数为P.i-1×P.i,对乘积A1·A2·A3···An以最小化标量乘法次数的方式进行加括号。

:矩阵链问题主要涉及的时在多个矩阵相乘,如何通过相乘的顺序来减少程序运行。

 

递推规律
设计算A[i:j](矩阵A从i乘到j),1≤i≤j≤n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n](上面例题就是m[1][6])。
      当i=j时,A[i:j]=Ai,因此,m[i][i]=0,i=1,2,…,n       当i<j时,若A[i:j]的最优次序在Ak和Ak+1之间断开,i<=k<j,则:m[i][j]=m[i][k]+m[k+1][j]+pi-1pkpj。由于在计算是并不知道断开点k的位置,所以k还未定。不过k的位置只有j-i个可能。因此,k是这j-i个位置使计算量达到最小的那个位置。
      综上,有递推关系如下:

 

测试 :A1 {30*35}    A2{35*15}    A3{15*5}   A4{5*10}   A5{10*20}    A6{20*25}

 

Input

含有多组测试数据,每组测试数据的第一列,含有1个整数N(N <= 10)代表有多少个数组要相乘。接下来有N对整数,代表一数组的列数及栏数。这N个数组的顺序与要你相乘的数组顺序是一样的。N=0代表输入结束。请参考Sample Input。

Output 

每组测试数据输出一列,内容为矩阵相乘的顺序(以刮号来表示)使得所用的乘法次数最小。如果有不只一组答案,输出任一组均可。请参考Sample Output。

Sample Input

3
1 5
5 20
20 1
3
5 10
10 20
20 35
6
30 35
35 15
15 5
5 10
10 20
20 25
0
#include <iostream>
#include<vector>
#include<cstring>
#include<stdio.h>
using namespace std;
int fz[100][100];
int point[100][100]; // 记录最小间断位置 只用1-n 空0号
int maj[100];
char temp;
void trace(int i,int j)
{
    if(i==j)
    {
            cout<<'A'<<i;
    }
     else
    {
        int k;
        cout<<'(';
        k=point[i][j];
        trace(i,k);
        cout<<" x ";
        trace(k+1,j);
        cout<<')';
    }
}
void maj1(int n)
 {
 int i,r,j,k;
    for(r=2;r<=n;r++) //r 间距距离
        for(i=1;i<=n-r+1;i++)
    {
        j=r+i-1;
        fz[i][j]=fz[i+1][j]+maj[i-1]*maj[i]*maj[j]; // 先把第一个,与后面分开
        point[i][j]=i;
        for(k=i+1;k<j;k++) //间断位置
        {
            int minsum=fz[i][k]+fz[k+1][j]+maj[i-1]*maj[k]*maj[j];
            if(minsum<fz[i][j])
            {
                fz[i][j]=minsum;
                point[i][j]=k;  //将间断位置存给point
            }
        }
    }
            trace(1,n);
    }
int main()
{
    int n;int i,j=1;
    int q=1;
    scanf("%d",&n);
    while(n!=0)
    {
            int a[2*n];
            int j=1;
            memset(maj,0,sizeof(maj));
            memset(point,0,sizeof(point));
            memset(fz,0,sizeof(fz));
            memset(a,0,sizeof(a));
            for(i=0;i<2*n;i++)
            {
                scanf("%d",&a[i]);
                fz[i][i]=0;
                point[i][i]=0;
             }
             maj[0]=a[0];
            for(i=1;i<2*n;)
                {
                    maj[j++]=a[i];
                    i=i+2;
                }

                printf("Case %d: ",q);
                q++;
                maj1(n);
                temp='0';
                cout<<'\n';
                scanf("%d",&n);
    }
}


 

推荐阅读