C++ program to solve the matrix-chain multiplication problem using
Dynamic Programming:

Import necessary libraries and namespaces

In [1]:
#include<stdio.h>
#include<limits.h>
#include<iostream>
using namespace std;

Recursive function to calculate nth Catalan number

In [2]:
// A recursive function to find nth catalan number 
int catalan(int n) 
{ 
    // Base case 
    if (n <= 1) return 1; 
  
    // catalan(n) is sum of catalan(i)*catalan(n-i-1) 
    int res = 0; 
    for (int i=0; i<n; i++) 
        res += catalan(i)*catalan(n-i-1); 
  
    return res; 
}

Implementation of dynamic approach recursively

  • Only optimal substructure property is used.
In [3]:
// Matrix Ai has dimension p[i-1] x p[i]  for i = 1..n 
int MatrixChainOrderRecursive(int p[], int i, int j) 
{ 
    if(i == j) 
        return 0; 
    int k; 
    int min = INT_MAX; 
    int count; 
  
    /* place parenthesis at different places  
       between first and last matrix, recursively  
       calculate count of multiplications for  
       each parenthesis placement and return the  
       minimum count */
    for (k = i; k < j; k++) 
    { 
        count = MatrixChainOrderRecursive(p, i, k)  
              + MatrixChainOrderRecursive(p, k + 1, j)  
              + p[i - 1] * p[k] * p[j]; 
  
        if (count < min) 
            min = count; 
    } 
  
    // Return minimum count 
    return min; 
}

Implementation of bottom-up dynamic approach

  • Optimal substructure used.
  • Overlapping subproblems handled in a bottom-up fashion.
In [4]:
// Matrix Ai has dimension p[i-1] x p[i] for i = 1..n
int MatrixChainOrder(int p[], int n)
{

    /* For simplicity of the program, one extra row and one
       extra column are allocated in m[][].  0th row and 0th
       column of m[][] are not used */
    int m[n][n];

    int i, j, k, L, q;

    /* m[i,j] = Minimum number of scalar multiplications needed
       to compute the matrix A[i]A[i+1]...A[j] = A[i..j] where
       dimension of A[i] is p[i-1] x p[i] */

    // cost is zero when multiplying one matrix.
    for (i=1; i<n; i++)
        m[i][i] = 0;

    // L is chain length.
    for (L=2; L<n; L++)
    {
        for (i=1; i<n-L+1; i++)
        {
            j = i+L-1;
            int r=i;
            m[i][j] = INT_MAX;
            for (k=i; k<=j-1; k++)
            {
                // q = cost/scalar multiplications
                q = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
                
                if (q < m[i][j])
                {
                    m[i][j] = q;
                    
                }
                
            }
            
        }
    }
     return m[1][n-1];
}

Demonstrating the above function

In [5]:
int arr[] = {1, 2, 3, 4,5,6,7,8,9,10,11,12,13,14,15,16};
In [6]:
for(int i=2;i<=15;i++)
{
    // Cost = Minimum number of multiplications needed.
    
    cout <<"Minimum number of multiplications(dp- recursive) is " << MatrixChainOrderRecursive(arr, 1, i - 1) << "\n"; 
    cout <<"Minimum number of multiplications(dp- bottom up) is : " << MatrixChainOrder(arr, i)  << "\n";
    cout << "\n";
}
Minimum number of multiplications(dp- recursive) is 0
Minimum number of multiplications(dp- bottom up) is : 0

Minimum number of multiplications(dp- recursive) is 6
Minimum number of multiplications(dp- bottom up) is : 6

Minimum number of multiplications(dp- recursive) is 18
Minimum number of multiplications(dp- bottom up) is : 18

Minimum number of multiplications(dp- recursive) is 38
Minimum number of multiplications(dp- bottom up) is : 38

Minimum number of multiplications(dp- recursive) is 68
Minimum number of multiplications(dp- bottom up) is : 68

Minimum number of multiplications(dp- recursive) is 110
Minimum number of multiplications(dp- bottom up) is : 110

Minimum number of multiplications(dp- recursive) is 166
Minimum number of multiplications(dp- bottom up) is : 166

Minimum number of multiplications(dp- recursive) is 238
Minimum number of multiplications(dp- bottom up) is : 238

Minimum number of multiplications(dp- recursive) is 328
Minimum number of multiplications(dp- bottom up) is : 328

Minimum number of multiplications(dp- recursive) is 438
Minimum number of multiplications(dp- bottom up) is : 438

Minimum number of multiplications(dp- recursive) is 570
Minimum number of multiplications(dp- bottom up) is : 570

Minimum number of multiplications(dp- recursive) is 726
Minimum number of multiplications(dp- bottom up) is : 726

Minimum number of multiplications(dp- recursive) is 908
Minimum number of multiplications(dp- bottom up) is : 908

Minimum number of multiplications(dp- recursive) is 1118
Minimum number of multiplications(dp- bottom up) is : 1118

Clearly, the 2 approaches don't differ widely upto small matrix size.
However, the recursive method fails to keep up for larger sizes.

To compare with the brute force method
We know from analysis of the brute force approach that it would require exponential time corresponding to the nth Catalan number.
So, we can print out the Catalan numbers recursively for the above sizes and contrast those values with the values for minimum number of multiplications obtained above.

In [7]:
for (int i=0; i<15; i++) 
        cout << "Catalan number C(" << i << ") : " << catalan(i) << "\n";
Catalan number C(0) : 1
Catalan number C(1) : 1
Catalan number C(2) : 2
Catalan number C(3) : 5
Catalan number C(4) : 14
Catalan number C(5) : 42
Catalan number C(6) : 132
Catalan number C(7) : 429
Catalan number C(8) : 1430
Catalan number C(9) : 4862
Catalan number C(10) : 16796
Catalan number C(11) : 58786
Catalan number C(12) : 208012
Catalan number C(13) : 742900
Catalan number C(14) : 2674440

Clearly, the corresponding Catalan numbers have greater numerical value.
Thus, the efficiency of the dynamic programming solution is established for matrix-chain multiplication problem.