#include <iostream>
#include <fstream>
using namespace std;
const int dim = 505;
const int INF = 1e9;
ifstream f( "podm.in" );
ofstream g( "podm.out" );
int dimens[ dim ]; ///matricea 1 are dimeniunile (dimens[1], dimens[2]). Ea se inmulteste cu matricea 2, cu dimensiunile (dimens[2], dimens[3]), ...
int dp[ dim ][ dim ], n;
/**
Matricea va avea pe diagonala principala 0, pe paralela la diag pp va avea prod care reprezinta nr de operatii necesar inm a doua matrici consec
Pe urmatoarea paralela la diag pp, nr minim de operatii necesar pt produsul inm a 3 matrici consec...
Daca am sirul de matrici A1 * A2 * A3
0 x y
0 z
0
x = dimens(A1) * dimens(A2), adica cel mai mic nr de inm pt A1 * A2
z = dimens(A2) * dimens(A3), adica cel mai mic nr de inm pt A2 * A3
y = cel mai mic nr de inm intre (A1 * A2) * A3 si A1 * (A2 * A3)
dp[ i ][ j ] = cel mai mic nr de inmultiri pentru segmentul de matrici A_i, A_(i + 1), A_(i + 2), ..., A_j. Adica, in momentul in care fac o rupere la poz k,
am doua subsiruri, A_i, ... A_(k - 1) si A_k, ... A_j, al caror numar optim este deja calculat, uitandu-ma pe linie, in stanga, si pe coloana, in jos.
3
2 3 4 1
dim A = (2, 3)
dim B = (3, 4)
dim C = (4, 1)
(A * B) => 24 inmultiri => dimens (2, 4)
(B * C) => 12 inmultiri => dimens (3, 1)
(A * B) * C <=> (2, 4) * (4, 1) => (2, 1) => 24 + 8 inmultiri (24 + 2*4*1)
A * (B * C) <=> (2, 3) * (3, 1) => (2, 1) => 6 + 12 inmultiri (2*3*1 + 12)
**/
void afiseaza_dim()
{
int i, j;
g << "Sirul de dimensiuni:" << endl;
for( i = 1; i <= n; ++i )
g << "(" << dimens[ i ] << ", " << dimens[ i + 1 ] << ") ";
g << endl << endl;
}
void afiseaza_prod_fracturare( int st, int dr, int x )
{
if( st <= x && x <= dr )
{
g << " Fracturez la indicele " << x << ", adica obtin intervalele de indici [" << st<< ", " << x << "], [" << x + 1 << ", " << dr << "]" << endl;
g << " Matricea rezultat, obtinuta prin prod matricilor dintre indicii [" << st << ", " << x << "] are dimensiunea (" << dimens[ st ] << ", " << dimens[ x + 1 ] << ")" << endl;
g << " Matricea rezultat, obtinuta prin prod matricilor dintre indicii [" << x + 1 << ", " << dr << "] are dimensiunea (" << dimens[ x + 1 ] << ", " << dimens[ dr + 1 ] << ")" << endl;
g << " Produsul de fracturare: " << dimens[ st ] * dimens[ x + 1 ] * dimens[ dr + 1 ] << endl;
g << endl << endl;
}
}
void afiseaza_dp()
{
int i, j;
for( i = 1; i <= n; ++i )
{
for( j = 1; j <= n; ++j )
if( dp[ i ][ j ] < INF )
g << dp[ i ][ j ] << " ";
else
g << "* ";
g << endl;
}
g << endl << endl;
}
int main()
{
int i, j, k, termen, prod3;
f >> n;
for( i = 1; i <= n + 1; ++i )
f >> dimens[ i ];
// afiseaza_dim();
for( i = 1; i < n; ++i )
dp[ i ][ i + 1 ] = dimens[ i ] * dimens[ i + 1 ] * dimens[ i + 2 ]; /// Se inm matr A_i cu matr A_(i+1) si se face un nr de inm egal cu acel prod
// afiseaza_dp();
/// Incep sa construiesc piramida incepand cu a doua para la DP
for( i = n - 2; i >= 1; --i )
for( j = i + 2; j <= n; ++j ) /// dp[i][i+1] sunt deja calculate
{
// g << "Calculez dp[" << i << "][" << j << "]" << endl;
dp[ i ][ j ] = INF;
for( k = i; k < j; ++k ) /// k este indicele la care facturez
{
prod3 = dimens[ i ] * dimens[ k + 1 ] * dimens[ j + 1 ]; /// Produsul intre ultimele doua matrici (dupa comprimarile necesare si anterioare)
// afiseaza_prod_fracturare(i, j, k);
// g << " Minimul intre cat era inainte si ( dp[" << i << "][" << k << "] + dp[" << k + 1 << "][" << j << "])" << " + produsul pentru fracturare";
// g << ", adica intre " << dp[ i ][ j ] << " si (" << dp[ i ][ k ] + dp[ k + 1 ][ j ] << ") + " << prod3 << endl;
termen = dp[ i ][ k ] + dp[ k + 1][ j ] + prod3; /// Inmultirea matricilor finale + inmultirea apsilor anteriori la parantezele puse
// g << " produsul de 3, termen = " << termen << endl;
dp[ i ][ j ] = min( dp[ i ][ j ], termen ); /// Retin doar minimul
// g << endl << " --> dp[" << i << "][" << j << "] = " << dp[ i ][ j ] << endl << endl;
}
// g << endl;
// afiseaza_dp();
}
g << dp[ 1 ][ n ]; /// Afisez doar varful piramidei
return 0;
}