矩阵链乘问题
输入:
共两行
第一行 N ( 1<=N<=100 ),代表矩阵个数。
第二行有 N+1 个数,分别为 A1 、 A2 ...... An+1 ( 1<=Ak<=2000 ), Ak 和 Ak+1 代表第 k 个矩阵是个 Ak X Ak+1 形的。
输出:
共两行
第一行 M ,为最优代价。注:测试用例中 M 值保证小于 2^31
第二行为最优顺序。如 (A1((A2A3)A4)) ,最外层也加括号。
注意:测试用例已经保证了输出结果唯一,所以没有AAA的情况.
共两行
第一行 N ( 1<=N<=100 ),代表矩阵个数。
第二行有 N+1 个数,分别为 A1 、 A2 ...... An+1 ( 1<=Ak<=2000 ), Ak 和 Ak+1 代表第 k 个矩阵是个 Ak X Ak+1 形的。
输出:
共两行
第一行 M ,为最优代价。注:测试用例中 M 值保证小于 2^31
第二行为最优顺序。如 (A1((A2A3)A4)) ,最外层也加括号。
注意:测试用例已经保证了输出结果唯一,所以没有AAA的情况.
input
6
30 35 15 5 10 20 25
output
15125
((A1(A2A3))((A4A5)A6))
6
30 35 15 5 10 20 25
output
15125
((A1(A2A3))((A4A5)A6))
思路分析:
1.分析最优解的结构
设计算A[1:n](第1项到第n项连成的最优解),那么它的子链A[1:k]和A[k+1:n]的次序也是最优的。
若不然,不妨设有一个计算比A[1:k]的次序计算量更少,则用它替换原来的A[1:k],那么A[1:n]的计算量
将比最优次序所需的计算量更少,与题设矛盾。因此最优子结构成立。
设计算A[1:n](第1项到第n项连成的最优解),那么它的子链A[1:k]和A[k+1:n]的次序也是最优的。
若不然,不妨设有一个计算比A[1:k]的次序计算量更少,则用它替换原来的A[1:k],那么A[1:n]的计算量
将比最优次序所需的计算量更少,与题设矛盾。因此最优子结构成立。
2.确定状态及状态方程
设问题即为状态m[i][j]表示从第i项连乘到第j项的最优计算次数。
设问题即为状态m[i][j]表示从第i项连乘到第j项的最优计算次数。
m[i][j] = 0 (i==j)
= min{m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j], 其中i<=k<j} (i<j)
= min{m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j], 其中i<=k<j} (i<j)
3.计算最优值
初始化 i:1->n m[i][i] = 0;
每次沿斜对角线向右上方计算,斜对角线标号变化:2->n
初始化 i:1->n m[i][i] = 0;
每次沿斜对角线向右上方计算,斜对角线标号变化:2->n
4.构造最优解
根据定义,递归输出最优解。
输出结果为(A[1:k]的最优次序,A[k+1:n]的最优次序)的格式
依据此形式编写递归函数进行输出。
注:在计算最优解的过程中注意记录求最优计算顺序的过程
根据定义,递归输出最优解。
输出结果为(A[1:k]的最优次序,A[k+1:n]的最优次序)的格式
依据此形式编写递归函数进行输出。
注:在计算最优解的过程中注意记录求最优计算顺序的过程
下边为循环由底向上的dp
#include <cstdio>
#include <iostream>
#define N 105
using namespace std;
int n;
int p[N], m[N][N], s[N][N];
void printTrace(int i, int j)//根据最优子结构确定
{
if (i == j)
{
printf("A%d", i);
return;
}
printf("(");
printTrace(i, s[i][j]);
printTrace(s[i][j] + 1, j);
printf(")");
}
int main()
{
scanf("%d", &n);
for (int i = 0; i <= n; i++)
scanf("%d", &p[i]);
if (n == 1)//特判
{
printf("0\n");
printf("(A1)\n");
return 0;
}
for (int i = 1; i <= n; i++)
m[i][i] = 0;
for (int r = 2; r <= n; r++)//r表示对角线的序号
{
for (int i = 1; i <= n - r + 1; i++)//当前斜线的元素个数的范围
{
int j = i + r - 1;//i与j,r的关系可以通过二维上三角求出
//初始化记录
m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];
s[i][j] = i;
for (int k = i + 1; k < j; k++)
{
int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
if (t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
}
}
printf("%d\n", m[1][n]);
printTrace(1, n);
printf("\n");
return 0;
}
下边是采用递归和记忆化搜索的方式dp
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 105
using namespace std;
int n;
int s[N][N], m[N][N], p[N];
void init()
{
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++)
m[i][j] = 0;
}
int matrixchain(int i, int j)
{
if (m[i][j] > 0) return m[i][j];
if (i == j) return 0;
int u = matrixchain(i, i) + matrixchain(i + 1, j) + p[i - 1] * p[i] * p[j];
s[i][j] = i;
for (int k = i + 1; k < j; k++)
{
int t = matrixchain(i, k) + matrixchain(k + 1, j) + p[i - 1] * p[k] * p[j];
if (t < u)
{
u = t;
s[i][j] = k;
}
}
return m[i][j] = u;
}
void printTrace(int i, int j)
{
if (i == j)
{
printf("A%d", i);
return;
}
printf("(");
printTrace(i, s[i][j]);
printTrace(s[i][j] + 1, j);
printf(")");
}
int main()
{
scanf("%d", &n);
for (int i = 0; i <= n; i++)
scanf("%d", &p[i]);
if (n == 1)//特判
{
printf("0\n");
printf("(A1)\n");
return 0;
}
init();
int ans = matrixchain(1, n);
printf("%d\n", ans);
printTrace(1, n);
printf("\n");
return 0;
}