UVA 348 矩阵链乘

时间:2022-07-30 08:13:40

UVA 348

题意:

给出 N 个矩阵(A1,A2,...,An),求完全括号化方案,使得计算乘积(A1A2...An)所需乘法次数最少。
并输出方案。

 

解题:

算法导论是个好东西 UVA 348 矩阵链乘  讲的很详细~

假设矩阵 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;
}