题意:n个矩阵链乘,求最少的乘法次数的计算顺序,将表达式输出。
题解:整个表达式中,一定有最后一次乘法,假设是第k个乘号,那么之前一定已经算出了P = A1 * A2 * ... * Ak 和 Q = Ak+1 * Ak+2 * ... An,由于P和Q的计算过程互不相干,所以再让P和Q也枚举出各自的最后以此乘法,一直分下去,这样可以找到状态转移方程 f(i, j) = min{f(i, k) + f(k + 1, j) + i.row * k.col * j.col},用一个数组将路径保存,path[l][r]是将从矩阵l到矩阵r的分割点的下标存起来,递归打印输出。
#include <stdio.h>
#include <string.h>
const int N = 15;
const int INF = 0x3f3f3f3f;
struct Array {
int row, col;
}arr[N];
int n, t = 1, f[N][N], path[N][N];
void path_print(int l, int r) {
if (l == r) {
printf("A%d", r);
return;
}
if (l + 1 == r) {
printf("(A%d x A%d)", l, r);
return;
}
printf("(");
path_print(l, path[l][r]);
printf(" x ");
path_print(path[l][r] + 1, r);
printf(")");
}
int dp(int l, int r) {
if (f[l][r] != -1)
return f[l][r];
int minn = INF;
for (int i = l; i < r; i++) {
int temp1 = dp(l, i);
int temp2 = dp(i + 1, r);
int temp = temp1 + temp2 + arr[l].row * arr[i].col * arr[r].col;
if (temp < minn) {
minn = temp;
path[l][r] = i;
}
}
f[l][r] = minn;
return f[l][r];
}
int main() {
while (scanf("%d", &n) && n) {
memset(f, -1, sizeof(f));
for (int i = 1; i <= n; i++)
scanf("%d%d", &arr[i].row, &arr[i].col);
f[n][n] = 0;
for (int i = 1; i < n; i++) {
f[i][i] = 0;
if (arr[i].col == arr[i + 1].row)
f[i][i + 1] = arr[i].row * arr[i].col * arr[i + 1].col;
else
f[i][i + 1] = 0;
}
dp(1, n);
printf("Case %d: ", t++);
path_print(1, n);
printf("\n");
}
return 0;
}