题目描述:
给定n个矩阵{A1,A2,…,An},其中,Ai与Ai+1是可乘的,(i=1,2 ,…,n-1)。用加括号的方法表示矩阵连乘的次序,不同的计算次序计算量(乘法次数)是不同的,找出一种加括号的方法,使得矩阵连乘的次数最小。
例如:
A1是A(5*10)的方阵;
A2是A(10*100)的方阵;
A3是A(100*2)的方阵;
那么有两种加括号的方法:
(A1A2)A3;
A1(A2A3);
第一种方法的计算量:5*10*100+5*100*2=6000;
第二种方法的计算量:10*100*2+5*10*2=2100;
可以看出不同计算方法计算量差别很大。
问题分析:
- 矩阵连乘的条件:第一个矩阵的列等于第二个矩阵的行,此时两个矩阵是可乘的;
- 多个矩阵连乘的结果矩阵,其行列等于第一个矩阵的行和最后一个矩阵的列;
- 两个矩阵相乘的计算量:
例如:A(3*2),B(2*4)
可知总执行次数为:3*2*4=24.
所以矩阵Am*n和Bn*k的乘法运算次数为:m*n*k;
思路
现给出数组p[] = { 30, 35, 15, 5, 10, 20, 25 },其中第0个数是第一个矩阵的行数,第一个到第六个分别为六个矩阵的列数
动态规划实现(自己写的,但是步骤基本一致)(时间复杂度较低)
public class matrixMultiply {
public static void main(String[] args) {
int p[] = { 30, 35, 15, 5, 10, 20, 25 };
int[][] m = new int[p.length][p.length];
int[][] s = new int[p.length][p.length];
multiply(m,s,p);
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m[0].length; j++) {
System.out.print(m[i][j]+"\t");
}
System.out.println();
}
System.out.println();
for (int i = 0; i < s.length; i++) {
for (int j = 0; j < s[0].length; j++) {
System.out.print(s[i][j]+"\t");
}
System.out.println();
}
}
private static void multiply(int[][] m, int[][] s, int[] p) {
for (int r = 3; r <= p.length; r++) { //链长(从3开始)
for (int begin = 0; begin <= p.length-r; begin++) { //链开始位置
int end = begin+r-1; //链结束位置
int temp1 = 0; //记录begin到end的链连乘的最少次数
int temp2 = 0; //记录练成次数最少时k的值
for (int k = begin+1; k <= end-1 ; k++) { //遍历分割位置k
m[begin][end] = m[begin][k]+m[k][end]+p[begin]*p[end]*p[k]; //计算时使用以前计算出的比现在的begin到end短的链的最少连乘次数
if(k==begin+1){
temp1=m[begin][end]; //赋初值
temp2 = k;
}else{
if(temp1>m[begin][end]){ //获取连乘次数最少
temp1 = m[begin][end];
temp2 = k;
}
}
}
m[begin][end] = temp1;
s[begin][end] = temp2;
}
}
}
}
迭代实现(时间复杂度较高)
public class matrixMultiply {
static int p[] = { 30, 35, 15, 5, 10, 20, 25 }; //这是六个矩阵的横纵长度,第0个数是第一个矩阵的行数,1到6分别是第1到第6个矩阵的列数
public static void main(String[] args) {
System.out.println(getMatrixChain(0, 6));
}
public static int getMatrixChain(int i, int j) {
int min = 0;
if (j-i<=1) { //链长小于或等于2时都返回0
return 0;
}
for (int r = i+1; r <= j-1; r++) { //r代表分割位置
int time = getMatrixChain(i, r) + getMatrixChain(r , j) //以r作为分界线将数组分为两部分
+ p[i] * p[r] * p[j]; //分割位置r处的乘法次数
//然后把r位置前的运算次数和r后的运算次数和r位置的运算次数加在一起即为总次数
if (r == i+1) { //给min赋初值
min = time;
}
if (min > time) {
min = time;
}
}
return min;
}
}