- 问题描述
-
给定一个有n个矩阵的矩阵链A1A2A3…An,其中矩阵Ai(i=1,2,3…n)的维度为pi-1*pi。我们知道,两个维度分别为m*r和r*n的矩阵用一般的矩阵乘法相乘,所需的运算次数为m*r*n,最后得到一个维度为m*n的结果矩阵。对于矩阵链问题,因为矩阵乘法具有结合律,其运算顺序有很多中选择。换句话说,不论如何括号其乘积,最后结果都会是一样的。例如,若有四个矩阵A、B、C和D,将可以有:
(ABC)D = (AB)(CD) = A(BCD) = A(BC)D = ...
但括号其乘积的顺序会影响到需要计算乘积所需简单算术运算的数目,即其效率。例如,设A为一10*30矩阵,B为30*5矩阵与C为5*60矩阵,则:
(AB)C有(10*30*5) + (10*5*60) = 1500 + 3000 = 4500 个运算 A(BC)有(30*5*60) + (10*30*60) = 9000 + 18000 = 27000 个运算 ...
明显地,第一种方式要有效多了。所以,矩阵链乘法问题也就是如何对矩阵乘积加括号,使得它们的乘法次数达到最少。
- 输入
-
输入的第一行为一个正整数n(1<=n<=200)。表示矩阵的个数。
输入的第二行包含n+1个整数,分别表示pi(0<=i<=n),其中每个pi在[1,200]范围内。 - 输出
-
输出一个整数表示最少要进行的乘法次数。
- 样例输入
-
3 1 2 3 4 3 10 30 5 60
- 样例输出
-
18 4500
- 题意理解了以后,接下来进行分析
- 就像刘汝佳大牛白书上写的那样
- 相乘很多次,一定存在一个最后一个乘号,设它的位置是k,那么,可以先把乘号左右两边的分别加上括号,分别对左右两边的括号,再去
- 细分,也就是说,每一个子括号也存在一个“最后乘号”
- 我们用dp[beg][end]来表示(beg,end)里的最优解
- 那么不难得到,当beg==end是,dp[beg][end]=0;
- 当beg!=end时,dp[beg][end]=min(dp[beg][end],dp[beg][k]+dp[k+1][end]+p[beg-1]*p[k]*p[end])
- 等式右边逗号后面的式子,是这样的,(beg,k)*(k+1,end),为什么是beg-1,下面会讲
- 矩阵的维度放在一个p[]数组里,从0开始记,第i个矩阵的维度是p[i-1]*p[i],所以,上面是beg-1
- 还有就是,循环是按区间长度递增来进行的,因为长区间总是依赖于短区间的嘛
- 代码:
-
大家可以去试试记忆化搜索的写法,反正我没写过,哟吼吼吼吼吼~~~~#include<stdio.h> int min(int a, int b) { return a < b ? a : b; } int main() { int n; //n个矩阵 while(~scanf("%d", &n)) { int i, j, k; int dp[210][210]; int p[210]; for(i = 0; i < n; i++) dp[i][i] = 0; for(i = 0;i <= n;i++) scanf("%d", &p[i]); for(j = 2; j <= n; j++) //j是长度 { for(i = 1; i <= n - j + 1; i++) //i是起始位置 { int end = i + j - 1; //end是结束位置 dp[i][end] = dp[i+1][end] + p[i-1] * p[end] * p[i]; for(k = i + 1; k < end; k++) { dp[i][end] = min(dp[i][end], dp[i][k] + dp[k+1][end] + p[i-1] * p[k] * p[end]); } } } printf("%d\n", dp[1][n]); } return 0; } <pre code_snippet_id="341584" snippet_file_name="blog_20140512_1_7770611">
NOJ [1003] 矩阵链乘法
最新推荐文章于 2023-12-23 18:59:31 发布