【动态规划】区间DP - 最优矩阵链乘(另附POJ1651Multiplication Puzzle)

it2026-01-15  8

最优矩阵链乘(动态规划)

一个 n ∗ m n*m nm的矩阵由 n n n m m m 列共 n ∗ m n*m nm 排列而成。两个矩阵A和B可以相乘当且仅当A的列数等于B的行数。一个nm的矩阵乘mp的矩阵,运算量为nmp。

矩阵乘法不满足分配律,但满足结合律。因此 A ∗ B ∗ C A*B*C ABC既可以按顺序 ( A ∗ B ) ∗ C (A*B)*C ABC 也可以按 A ∗ ( B ∗ C ) A*(B*C) ABC 来进行。假设 A 、 B 、 C A、B、C ABC 分别是 2 ∗ 3 、 3 ∗ 4 、 4 ∗ 5 2*3、3*4、4*5 233445 的,则 ( A ∗ B ) ∗ C (A*B)*C ABC 运算量是 2 ∗ 3 ∗ 4 + 2 ∗ 4 ∗ 5 = 64 2*3*4+2*4*5=64 234+245=64 A ∗ ( B ∗ C ) A*(B*C) ABC的运算量是 3 ∗ 4 ∗ 5 ∗ 2 ∗ 3 ∗ 5 = 90 3*4*5*2*3*5=90 345235=90 .显然第一种顺序节省运算量。

给出n个矩阵组成的序列,设计一种方法把他们依次乘起来,使得总的运算量尽量小。假设第i个矩阵A[i]是 P [ i − 1 ] ∗ P [ i ] P[i-1]*P[i] P[i1]P[i]的(意味着这些矩阵可以按顺序链乘,所以只需要考虑区间相乘中区间的选取)。

输入

3 2 3 4 5

输出

64

实际上就是区间DP的模板。 我们用 f ( i , j ) f(i,j) f(i,j)表示A[i]、A[i+1]...A[j]乘起来的最少运算数

状态转移方程

f(i,j)=min{f(i,j),f(i,k) + f(k+1,j) + P[i-1]*P[k]*P[j]}(i=<k<j)

边界为 f ( i , i ) = 0 f(i, i) = 0 f(i,i)=0

我们使用闫氏DP分析法,实际上我们只需要考虑最后一步的操作即可。

本题我没有找到题目,找到了一道POJ上和这个几乎一模一样的题目,只不过那个题目是输入n只有n个数,所以相当于只有n-1个矩阵,所以会有微小的差别。

所以我们这里的答案是f[1][n - 1],转移方程的时候也有差别:

f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + a[i] * a[k + 1] * a[j + 1]); #include<cstdio> #include<cmath> #include<algorithm> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N = 2007, M = 5000007, INF = 0x3f3f3f3f; int n, m; int f[N][N]; int a[N]; int main() { while(scanf("%d", &n) != EOF && n){ memset(f, 0x3f, sizeof f); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); f[i][i] = 0; } for(int len = 2; len < n; ++ len){ for(int i = 1; i <= n - len; ++ i){ int j = i + len - 1;//注意再 -1 因为三个数才能组成两个可乘矩阵相乘 f[i][j] = INF; for(int k = i; k < j; ++ k) //!相当于是i到j但是需要的是i到j+1:例如第1个和第2个合并,对应到数上就是a[1]a[2]a[3]合并 f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + a[i] * a[k + 1] * a[j + 1]); } } printf("%d\n", f[1][n - 1]);//终点是 n-1 因为三个数才能组成两个可乘矩阵相乘 } return 0; }
最新回复(0)