Go: package main

/*
連鎖行列積
*/

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"math"
	"os"
	"strconv"
	"strings"
)

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

type mem *[][]int

func main() {
	scanner := makeScanner(1024)
	n := eGetInt(scanner)
	p := make([]int, n+1)
	for i := 0; i < n; i++ {
		rowcol := eGetInts(scanner)
		p[i] = rowcol[0]
		p[i+1] = rowcol[1]
	}
	m := make([][]int, n)
	for i := 0; i < n; i++ {
		m[i] = make([]int, n)
	}
	// 初期値の格納
	for i := 0; i < n; i++ {
		for j := i; j < n; j++ {
			if i == j {
				m[i][j] = 0 //  同じ行列へのコストは当然0
			} else {
				m[i][j] = math.MaxInt64
			}
		}
	}

	// num : 行列の数. num == 1の場合は初期値0を格納ずみ
	for num := 2; num <= n; num++ {
		// s : スタート行列番号
		for s := 0; s+num <= n; s++ {
			// t : 終了行列番号
			t := s + num - 1
			// k : 仕切
			for k := s; k < t; k++ {
				m[s][t] = min(m[s][t], m[s][k]+m[k+1][t]+p[s]*p[k+1]*p[t+1])
			}
		}
	}
	fmt.Println(m[0][n-1])
}

////////////////////////////////////////////////////////////////////////////////
// ライブラリ
////////////////////////////////////////////////////////////////////////////////
func makeScanner(maxByte int) *bufio.Scanner {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Buffer(make([]uint8, 0, 8192), maxByte)
	return scanner
}
func makeWordScanner() *bufio.Scanner {
	scanner := makeScanner(8192)
	scanner.Split(bufio.ScanWords)
	return scanner
}
func eGetLine(r *bufio.Scanner) string {
	if r.Scan() {
		return r.Text()
	}
	err := r.Err()
	if err != nil {
		log.Fatal(err)
	}
	// EOF
	return ""
}
func eGetInt(r *bufio.Scanner) int {
	line := eGetLine(r)
	return eAtoi(line)
}
func eGetFields(r *bufio.Scanner) []string {
	line := eGetLine(r)
	return strings.Fields(line)
}
func eGetInts(r *bufio.Scanner) []int {
	fields := eGetFields(r)
	ints := make([]int, len(fields))
	for i := 0; i < len(ints); i++ {
		ints[i] = eAtoi(fields[i])
	}
	return ints
}
func eGetChars(r *bufio.Scanner) []string {
	line := eGetLine(r)
	return strings.Split(line, "")
}
func eAtoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		log.Fatal(err)
	}
	return n
}

type ints []int

func (s ints) String() string {
	var buf bytes.Buffer
	buf.WriteString("[")
	sep := ""
	for _, v := range s {
		buf.WriteString(fmt.Sprintf("%s%d", sep, v))
		sep = ", "
	}
	buf.WriteString("]")
	return buf.String()
}

func reverse(a ints) ints {
	r := make(ints, len(a))
	for i := 0; i < len(a); i++ {
		r[i] = a[len(a)-1-i]
	}
	return r
}
func max(a ints) (int, bool) {
	if len(a) == 0 {
		return 0, false
	}
	m := a[0]
	for _, e := range a {
		if e > m {
			m = e
		}
	}
	return m, true
}

// func min(a []int) int {
// 	m := a[0]
// 	for _, v := range a {
// 		if v < m {
// 			m = v
// 		}
// 	}
// 	return m
// }

func sum(a ints) int {
	if len(a) == 0 {
		return 0
	}
	sum := 0
	for _, e := range a {
		sum += e
	}
	return sum
}
func find(n int, a ints) int {
	for i, v := range a {
		if n == v {
			return i
		}
	}
	return -1
}

C++: #include <algorithm>
#include <iostream>
#include <vector>

int main() {
  uint32_t n = 0;
  std::cin >> n;
  std::vector<uint32_t> mat(n + 1);
  for (uint32_t i = 0; i < n; i++) {
    std::cin >> mat[i] >> mat[i + 1];
  }

  std::vector<std::vector<uint32_t>> dp(n + 1,
                                        std::vector<uint32_t>(n + 1, -1));
  for (size_t i = 0; i < dp.size(); i++) dp[i][i] = 0;

  for (size_t d = 1; d < n; d++) {
    for (size_t i = 1; i < dp.size() - d; i++) {
      for (size_t j = 0; j < d; j++) {
        dp[i][i + d] =
            std::min(dp[i][i + d], mat[i - 1] * mat[i + j] * mat[i + d] +
                                       dp[i][i + j] + dp[i + j + 1][i + d]);
      }
    }
  }

  std::cout << dp[1][n] << std::endl;

  return 0;
}

C++: #include <bits/stdc++.h>
using namespace std;

int main(void) {
	int n, i, j, k, len;
	cin >> n;
	int p[n + 1], m[n + 1][n + 1];
	for (i = 0; i <= n; i++)
	{
		if (i == 0) {
			cin >> p[i];
		} else if (i == 1) {
			cin >> p[i];
		} else {
			cin >> p[i] >> p[i];
		}
	}

	for (i = 1; i <= n; i++) 
		m[i][i] = 0;

	for (len = 2; len <= n; len++) {
		for (i = 1; i <= n - len + 1; i++) {
			j = i + len - 1;
			m[i][j] = INT_MAX;
			for (k = i; k < j; k++) {
				m[i][j] = min(m[i][j], m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]);
			}
		}
	}
	cout << m[1][n] << endl;
}

C++: /**
 * 附上C++做法, c++的cin和string真好用，比java理解起来难，但是操作起来舒服很多，
 * java的string是immutable的这点比较麻烦
 * 感觉c++跟其他所有面向对象语言都不太一样
 */
#include <iostream>
#include <string>
#include <vector>
#include <map>

using namespace std;

#include <iostream>
#include <set>
#include <string>
#include <climits>

#include <iostream>
#include <set>
#include <string>
#include <climits>


/**
 * Given two strings str1 and str2 of the same length, determine whether you can transform str1 into str2 by doing zero or more conversions.

In one conversion you can convert all occurrences of one character in str1 to any other lowercase English character.

Return true if and only if you can transform str1 into str2.



Example 1:

Input: str1 = "aabcc", str2 = "ccdee"
Output: true
Explanation: Convert 'c' to 'e' then 'b' to 'd' then 'a' to 'c'. Note that the order of conversions matter.
Example 2:

Input: str1 = "leetcode", str2 = "codeleet"
Output: false
Explanation: There is no way to transform str1 to str2.


Note:

1 <= str1.length == str2.length <= 10^4
Both str1 and str2 contain only lowercase English letters.


6
30 35
35 15
15 5
5 10
10 20
20 25
 */

#include <vector>
#include <unordered_map>

//矩阵连乘问题，递归方程采用课本P47的递归方程。
//与课本P47的程序相比，两者都是填m[][]的上三角，
//但本程序是横向填表，而课本程序是斜向填表。
//本程序按照 自底向上，自左向右 的顺序来计算m[][]的上三角。
//程序中有记录相应断开位置到s[][]。

#include <iostream>

using namespace std;

void MatrixChain(int, int[], int **);

void CalculateMin(int i, int j, int dimen[], int **dp);


//void MatrixChain(int n, int dimen[], int **dp) {
//    //以下双重循环对m[][]的上三角进行填表
//    //填表顺序为自底向上，自左向右
//    for (int i = n; i >= 1; i--) {//i表示行
//        for (int j = i; j <= n; j++) {//j表示列，因为只填上三角，所以j的初值为i
//            //以下按照矩阵连乘问题的递归公式来求每一个m[i][j]
//            if (i == j) {
//                cout << sizeof(dp) << endl;
//                cout << dp[i][j] << endl;
//                dp[i][j] = 0;
//                cout << dp[i][j] << endl;
//                cout << 44 << endl;
//
//            } else {
//                CalculateMin(i, j, dimen, dp);
//            }
//        }
//    }
//}

void matrixChain(int n, int dimen[], int **dp) {
    for (int len = 0; len <= n; ++len) {
        for (int i = 1; i + len <= n; ++i) {
            int j = i + len;
            if (i == j) {
                dp[i][j] = 0;
            } else {
                CalculateMin(i, j, dimen, dp);
            }
        }
    }
}


void CalculateMin(int i, int j, int dimen[], int **dp) {
    dp[i][j] = dp[i][i] + dp[i + 1][j] + dimen[i - 1] * dimen[i] * dimen[j];
    for (int k = i + 1; k < j; k++) {//k用于尝试不同的断开位置
        int curr = dp[i][k] + dp[k + 1][j] + dimen[i - 1] * dimen[k] * dimen[j];
        if (curr < dp[i][j]) {//当前k值作为Ai连乘到Aj的断开位置能获得更少计算量
            dp[i][j] = curr; //刷新最优值
            //pivot[i][j] = k;   //刷新相应断开位置
        }
    }
}

int main() {
    int n;
    cin >> n;
    int *tempP = new int[2 * n]; //dimen[0]为A1的行数；dimen[i]为Ai的列数
    int *dimen = new int[n + 1];    //dimen[0]为A1的行数；dimen[i]为Ai的列数
    int **dp = new int *[n + 1]; //dp[i][j]为Ai连乘到Aj的最少乘次数
    for (int i = 1; i <= n; i++) {
        dp[i] = new int[n + 1];
    }
    for (int i = 0; i < 2 * n; i++) {
        cin >> tempP[i];
    }
    for (int i = 0; i <= n; i++) {
        dimen[i] = i == 0 ? tempP[0] : tempP[i * 2 - 1];
    }
    matrixChain(n, dimen, dp);
    cout << dp[1][n] << endl; //最优值存储在m[1][n]
    return 0;
}