给定n个矩阵 {A1,A2,…,An} ,其中Ai与Ai+1是可乘的(i=1,2,…,n-1)。由于矩阵乘法满足结合律,所以它们的连乘积 A1A2…An有不同的计算次序。不同计算次序需要的乘法次数不同,求使乘法次数最少的计算次序。
1.第一行:矩阵个数n
2.第二行: (n+1)个数字 p[n+1] ,其中p[i-1]和p[i]表示第 i 个矩阵的行和列数。
最少乘法次数 和 对应计算次序。
#include
#include
void matrixChain(int n, int *p, int **m, int **s);
void Traceback(int i,int j,int **s);
int main(int argc, char const *argv[])
{
int n;
printf("请输入矩阵个数:");
scanf("%d", &n);
// n个矩阵连乘,第i个矩阵行列数分别为p[i-1]和p[i]
int *p = (int *)malloc((n + 1) * sizeof(int));
// m[i][j]表示计算A[i:j]所需最少乘次数
int **m = (int **)malloc((n + 1) * sizeof(int *));
// s[i][j]表示计算A[i:j]断开的位置
int **s = (int **)malloc((n + 1) * sizeof(int *));
printf("请输入表示行列的 n+1 个数字:");
for (int i = 0; i <= n; i++)
{
scanf("%d", &p[i]);
m[i] = (int *)malloc((n + 1) * sizeof(int));
s[i] = (int *)malloc((n + 1) * sizeof(int));
}
matrixChain(n, p, m, s);
printf("最少需要乘 %d 次\n", m[1][n]);
Traceback(1, n, s);
return 0;
}
void matrixChain(int n, int *p, int **m, int **s)
{
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;
//A[i]和A[i+1:j]
int u = m[i + 1][j] + p[i - 1] * p[i] * p[j];
s[i][j] = i;
// printf("m[%d][%d] = %d\n", i, j, m[i][j]);
for (int k = i + 1; k < j; k++)
{ // A[i:j]于第 k 个矩阵断开
// 计算A[i:k]和A[k+1:j]最少乘法次数+二者相乘乘法次数
int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
if (t < u)
{ // 依次寻找乘法次数最少的断点
u = t;
s[i][j] = k;
}
}
m[i][j] = u;
printf("从 %d 到 %d,在 %d 断开,m[%d][%d] = %d\n", i, j, k, i, j, m[i][j]);
}
}
}
void Traceback(int i,int j,int **s)
{
if (i == j)
{ // 单个矩阵,输出
printf("A%d", i);
return;
}
printf("(");
Traceback(i, s[i][j], s); // 输出断点前
Traceback(s[i][j] + 1, j, s); // 输出断点后
printf(")");
}
该方法是动态规划的变形,都是记录查表。不同的是,备忘录方法是自顶而下的。当所有子问题都需要求解时,两种方法没太大区别。但由于备忘录递归调用,一般使用动态规划。备忘录方法适合需要剪枝的场合。
int LookupChain(int i, int j, int **m, int **s, int *p)
{
if (m[i][j] > 0) return m[i][j];
if (i == j) return 0;
int u = LookupChain(i, i, m, s, p) + LookupChain(i + 1, j, m, s, p) + p[i - 1] * p[i] * p[j];
// int u = 0 + LookupChain(i + 1, j, m, s, p) + p[i + 1] * p[i] * p[j];
s[i][j] = i;
for (int k = i + 1; k < j; k++)
{
int t = LookupChain(i, k, m, s, p) + LookupChain(k + 1, j, m, s, p) + p[i - 1] * p[k] * p[j];
if (t < u)
{
u = t;
s[i][j] = k;
}
}
m[i][j] = u;
return u;
}
int MemorizeMatrixChain(int n, int **m, int **s, int *p)
{
for (int i = 1; i < n; i++)
for (int j = 1; j <= n; j++)
m[i][j] = 0;
return LookupChain(1, n, m, s, p);
}