设A1,A2,...,An为n个矩阵的序列,其中Ai为pi-1Xpi阶矩阵,i=1,2,...,n.这个矩阵的输入用向量P=<p0,p1,..,pn>给出,其中p0是矩阵A1的行数,pi(i=1,2,...,n-1)既是矩阵Ai的列数也是矩阵Ai+1的行数,最后的pn是矩阵An的列数;
例:P=<30,35,15,5,10,20>
则对应的矩阵序列:A1:30X35 A2:35X15 A3:15X5 A4:5X10 A5:10X20
对于该矩阵序列A1XA2XA3XA4XA5的乘法满足结合率,所以无论采用哪种顺序结果都是一样的,但是计算的工作量是不一样的.
比如A1:10X100 A2:100X5 A3:5X50
以元素相乘作为基本运算:
(A1A2)A3的工作量是10X100X5+10X5X50=7500
A1(A2A3)的工作量是10X100X50+100X5X50=75000
工作量相差10倍.
现在我们要找出一种乘法的顺序使得计算量最少.
1.蛮干法时间复杂度O(2^2n/n^(3/2)) 指数的时间
2.动态规划的时间复杂度O(n^3) 多项式的时间
下面就是使用动态规划的方法来解决:
package com.动态规划;
public class MatrixChain {
//记录A[i][j]所需最小运算次数
private int min[][] ;
//最后一次运算的位置
private int s[][];
//存放矩阵的输入向量
private int p[];
//存放矩阵的个数
private int num;
public MatrixChain(int[] p){
if(null==p||p.length==1){
return ;
}
//矩阵的个数为如数向量的个数减1
int num = p.length-1;
//初始化min[][],s[][]
min = new int[num+1][num+1];
s = new int[num+1][num+1];
for(int i=1;i<=num;i++){
for(int j=1;j<=num;j++){
min[i][j] = 0;
s[i][j] = 0;
}
}
this.num = num;
this.p = p;
//进行计算
matrixchain();
}
//关键代码
private void matrixchain(){
//r代表当前计算的链长(子问题规模)
for(int r=2;r<=num;r++){
//num-r+1表示最后一个r长的链的前边界
for(int i=1;i<=num-r+1;i++){
int j = i+r-1;//计算前边界为i长度为r的链的后边界
min[i][j] = min[i+1][j]+p[i-1]*p[i]*p[j];//划分为Ai(Ai+1...Aj)
s[i][j] = i;
//更新最小的子问题的乘积次数
for(int k=i+1;k<=j-1;k++){//i<k<j
int t = min[i][k]+min[k+1][j]+p[i-1]*p[k]*p[j];//划分为(Ai...Ak)(Ak+1...Aj)
if(t<min[i][j]){//若该种划分比原来的划分所取得的值小则更新
min[i][j] = t;//更新最小值
s[i][j] = k;//更新备忘录
}
}
}
}
}
public void printMatrix(){
System.out.println("-----------------------------");
print(this.min);
System.out.println("-----------------------------");
}
public void printTrace(){
System.out.println("-----------------------------");
print(this.s);
System.out.println("-----------------------------");
}
private void print(int list[][]){
for(int i=1;i<list.length;i++){
for(int j=1;j<list.length;j++){
System.out.print("["+i+"]"+"["+j+"]:"+list[i][j]+" ");
}
System.out.println();
}
}
public static void main(String[] args) {
int[] p = {30,35,15,5,10,20};
MatrixChain mc = new MatrixChain(p);
mc.printMatrix();
mc.printTrace();
}
}
结果:
-----------------------------
[1][1]:0 [1][2]:15750 [1][3]:7875 [1][4]:9375 [1][5]:11875
[2][1]:0 [2][2]:0 [2][3]:2625 [2][4]:4375 [2][5]:7125
[3][1]:0 [3][2]:0 [3][3]:0 [3][4]:750 [3][5]:2500
[4][1]:0 [4][2]:0 [4][3]:0 [4][4]:0 [4][5]:1000
[5][1]:0 [5][2]:0 [5][3]:0 [5][4]:0 [5][5]:0
-----------------------------
-----------------------------
[1][1]:0 [1][2]:1 [1][3]:1 [1][4]:3 [1][5]:3
[2][1]:0 [2][2]:0 [2][3]:2 [2][4]:3 [2][5]:3
[3][1]:0 [3][2]:0 [3][3]:0 [3][4]:3 [3][5]:3
[4][1]:0 [4][2]:0 [4][3]:0 [4][4]:0 [4][5]:4
[5][1]:0 [5][2]:0 [5][3]:0 [5][4]:0 [5][5]:0
-----------------------------
上面一个矩阵是min
下面一个矩阵是s
可以看到[1][5]:11875 就是该矩阵链乘积工作量的最小值
通过min,s来分析划分的次序:
首先看到min[1][5]表示该矩阵链A1...A5的最小的工作量,对应的s[1][5]=3,表示该划分在A3的后面,即是(A1A2A3)(A4A5),再看s[1][3]=1,即是A1(A2A3)(A4A5)
更详细的讲解请参考算法设计与分析第3章但是上面有一点小错误我在代码中已经修改,请对照看.