UVA 348 矩阵链乘
UVA 348
题意:
给出 N 个矩阵(A1,A2,...,An),求完全括号化方案,使得计算乘积(A1A2...An)所需乘法次数最少。
并输出方案。
解题:
算法导论是个好东西 讲的很详细~
假设矩阵 A 和 B 相乘,那 A 的列数必须要和 B 的行数相同,
即 若 A 的行列数为(x,y),则 B 须为(y,z), 它们相乘得到一个(x,z)的矩阵;需要乘 xyz 次。
题目把相乘的顺序给出了,我们做的就是添加括号了。
把所有矩阵用一个序列 P 表示,第 i 个矩阵的行列数为 P(i-1)和Pi
求 N 个矩阵的完全括号化方案,
当 N 为 1 时,只有一个矩阵,所以只有 1 种方案;
当 N >= 2 时,可描述为两个已经完全括号化的部分积相乘的形式。
设这个划分点在 k ;那么 A1..Ai..An = (A1..Ak)(Ak+1..An)
然后同样的看 (1~k)(k+1 ~ N)这两段。
用 m[i][j] 保存(i,j)这个区间括号化后可以得到的最小次数.
于是 m[i][j] = m[i][k] + m[k+1][j] + P(i-1)PkPj;
再用一个 数组记录 (i,j)这个区间的划分点在哪递归输出。
我角的它就是个区间DP呀
#include <bits/stdc++.h> using namespace std; const int maxn = 110; const int INF = 0x3f3f3f3f; int p[maxn]; int m[20][20],s[20][20]; int x, n, cas = 1, cnt = 0; void print (int x, int y) { if(x == y) printf ("A%d",x); else { printf ("("); print (x, s[x][y]); if(s[x][y]) printf (" x "); print (s[x][y]+1, y); printf (")"); } } int main() { while (scanf("%d",&n)!=EOF && n) { cnt = 0; scanf ("%d%d",&p[0], &p[1]); for (int i = 1; i < n; i ++) { scanf ("%d%d",&x,&p[i+1]); } for (int i = 1; i <= n; i ++) { for (int l=1, r; (r = l + i) <= n; l ++) { m[l][r] = INF; for (int k = l; k < r; k ++) { int tmp = m[l][k] + m[k+1][r] + p[l-1] * p[k] * p[r]; if (tmp < m[l][r]) { m[l][r] = tmp; s[l][r] = k; } } } } printf ("Case %d: ",cas++); print (1, n); printf ("\n"); } return 0; }