问题描述
实现矩阵连乘算法。输入n个矩阵的维度(n+1个数, n>2),输出矩阵连乘的最小数乘次数以及连乘次序,即矩阵加括号的方式,如: (A1((A2A3)A4))。
提交格式:
实现void solve(int n,int p[],int out[])函数。
参数n为矩阵个数,p为关于矩阵维数的n+1个数,矩阵Ai的维数为(p[i-1],p[i]),out[0]为最小数乘次数MOD1000000007,out[i]为输出的连乘次序(i>0)。2<n<=500,2<p[i]<=500。
输出连乘次序时,请将(替换为-1、)替换为-2,矩阵Ai替换为i,输出到out数组中。
请不要printf输出任何内容。
输入样例:
输出样例:
1
| out={292,-1,-1,1,2,-2,-1,-1,3,4,-2,5,-2,-2}
|
核心思路
- 最优子结构分析,建立递归关系,得到递归表达式;
- 依据其递归式以自底向上的方式进行迭代,得到m矩阵和s矩阵;
- 通过s矩阵加括号;
- 构造主函数进行调试。
实现要点
- 关键在于构造递推表达式,这也是动态规划的难点所在;
- 由于数据可能比较大,数据类型选择long long;
- 虽然题目是加数字,但和加括号一样,借助通过递归实现。
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include<stdio.h> #include <stdlib.h>
long long m[500][500], s[500][500]; long long number; void traceback(long long i, long long j, int* out); void solve(int n, int p[], int out[]) { for (long long i = 1; i <= n; i++) m[i][i] = 0; for (long long r = 1; r <= n - 1; r++) for (long long i = 1; i <= n - r; i++) { long long j = i + r; m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j]; s[i][j] = i; for (long long k = i + 1; k < j; k++) { long long 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; } } } out[0] = m[1][n]% 1000000007; for (long long i = 1; i <= n; i++) { out[i] = i; } number = n; traceback(1,n,out); }
void traceback(long long i, long long j, int* out) { long long k; if (i == j) return ; else { for (k = number; out[k + 1] != i; k--) { out[k + 1] = out[k]; } out[k+1] = -1; number++; for (k = number; out[k] != j; k--) { out[k + 1] = out[k]; } out[k + 1] = -2; number++; traceback(i, s[i][j], out); traceback(s[i][j] + 1, j, out); } }
int main() { int n=5; int p[6] = { 10,5,2,8,6,3 }; int out[1500] = {0}; solve(n, p, out); printf("out="); for (int i = 0; i <= number; i++) { printf("%d,", out[i]); } system("pause"); return 0; }
|