3. 矩阵连乘问题
(1) 矩阵链连乘问题:
给定n个矩阵{A1,A2,...,An},其中Ai与Ai+1是可乘 的,i=1,2...,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序 计算矩阵连乘积需要的数乘次数最少。
输入数据:共m+1行;第一行为测试数据的组数m;以后每行n+1个正 整数,表示n个矩阵的行列值。
输出:最少次数及连乘的计算次序。
样例输入:
1
5 10 4 6 10 2
样例输出:
348
(A1(A2(A3(A4A5))))
思路:
1.最优子结构:将AiAi+1...Aj 记为 A[ i : j ]。计算 A[ 1 : n ]的最优计算序列,设这个计算次序在矩阵Ak(1<= k <= n)和 Ak+1之间把矩阵连乘断开,其相应的加括号的方式为( ( A1+...+Ak ) ( Ak+1+...+An ) )。它的子链也是最优的(反证法)。
Count(A[1:n]) = Count(A[1:k])+ Count(A[k+1:n])+ Count(A[1:k] * A[k+1:n])
2.递推关系式:
记对于A[ i : j ] 所需的最少乘法次数为m[ i ][ j ]。
m[ i ][ j ] = 0 (i == j)
m[ i ][ j ] = min { m[ i ][ k ] + m[ k+1 ][ j ] + pi-1 pk pj } (i ≤ k ≤ j,i < j)
3.计算最优值
直接递归耗时O(n2),采取记忆化保存结果。
Code:
#include<iostream> #include<vector> #include<cstring> using namespace std; int INF = 0x3f3f3f3f; const int maxN = 1000; vector<int> a; int m[maxN][maxN]; int s[maxN][maxN]; int n; struct pos{ int front; int back; }*p; void extract (string &s) { for (int i = 0; i < s.size(); i++){ int x = 0; while (s[i] != ' ' && i < s.size()){ x *= 10; x += s[i] - '0'; i++; } a.push_back(x); } } void Trace(int i, int j) { if (i == j) return; Trace(i, s[i][j]); Trace(s[i][j]+1, j); //cout << "Mutbiply A "<<i<<", "<<s[i][j]; //cout << "and A "<<(s[i][j]+1)<<", "<<j<<endl; p[i].front++; p[j].back++; } void solve() { for (int r = 2; r <= n; r++) { for (int i = 1; i <= n-r+1; i++) { int j = i+r-1; m[i][j] = m[i+1][j] + a[i-1] * a[i] * a[j]; s[i][j] = i; for (int k = i+1; k < j; k++) { int t = m[i][k] + m[k+1][j] + a[i-1] * a[k] * a[j]; if (t < m[i][j]) { m[i][j] = t; s[i][j] = k; } } } } } int main() { int cnt; scanf("%d", &cnt); getchar(); while (cnt--) { string ss; getline(cin, ss); memset(m, 0, maxN); memset(s, 0, maxN); res.clear(); a.clear(); extract(ss); n = a.size()-1; p = new pos[n+1]; for (int i = 0; i <= n; i++) p[i].front = p[i].back = 0; solve(); /*for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) cout << m[i][j]<< ' '; cout << endl; }*/ cout << m[1][n] << endl; Trace(1,n); for (char i = '1'; i <= '0'+n; i++){ int index = i - '0'; while(p[index].front --) cout << '('; cout << 'A' << i; while(p[index].back--) cout << ')'; } } return 0; }
人生不如意的时候,是上帝给的长假,这个时候应该好好享受假期。
突然有一天假期结束,时来运转,人生才是真正开始了。
突然有一天假期结束,时来运转,人生才是真正开始了。