算法[动态规划]-矩阵连乘问题

动态规划算法的基本要素

(1):最优子结构性质
(2):重叠子问题性质

动态规划法解题思路

(1):找出最优解的性质,并刻画其结构特征
(2):递归的定义最优值
(3):以自底向上的方式计算出最优值
(4):根据计算最优值得到的信息,构造最优解

动态规划与分治的主要区别

(1):分治的自顶向下进行计算的,动态规划是自底向上进行计算的
(2):动态规划法记录了已解决的子问题的解,分治法不记录

矩阵连乘问题的描述

给定N个矩阵A1,A2…An,其中,A(i)和A(i+1)是可乘的,确定计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少

解决思路

1:将N个矩阵的行列数存入一个大小为N+1的一维数组A中,即:A[0]为第一个矩阵的行数,A[1]为第一个矩阵的列数,又因为第一二个矩阵是可乘的,所以同时又是第二个矩阵的行数,依次存入N个矩阵的行列数
申请一个规格为(N+1) (N+1)的二维数组M用以记录动态规划中的一步步所得的子问题的解,规格为N+1是因为便于在填表时方便易懂。第0行,第0列不填。
再申请一个此规格的二维数组S,用以存储得出的最优子结构的分割点
2:我们再来详细解释一下1中的二维数组M的意义,M[i][j]的意义则代表从第i个矩阵乘到第j个矩阵所乘的最小次数,所以,我们最终所需的结果便是M[1][6]。
由上述亦可得,M表沿对角线左下部分是无用的,对角线上M[i][i]=0,S表亦是。
填完对角线的所有0之后,我们可以考虑填沿对角线向右上方向上的一层,比如M[1][2],M[2][3],M[3][4]…这些我们都可以直接填,因为,它们没有其他解可以比较
沿右上方向,我们再走一层,此时,我们考虑M[1][3],这时,我们知道我们可以(A1 A2) A3 即当2做cut点时,也可以A1 (A2 A3) 即当1做cut点时 。这时我们将这两种情况所得结果比较,取得最小次数存入。
这两种情况的结果如何得到呢?我们可以有这样的公式(当k为cut点时)times=M[i][k] + M[k+1][j] + A[i-1] A[k] * A[j]
这样,我们就可以依次填表,最后得到了M[1][N]

C代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int Matrix(int *A,int **M,int **S,int n)
{
int i,l,j,k,q;
for(i = 1;i < n; i++){ //将对角线置为0
M[i][i] = 0;
}
for(l = 2;l < n; l++){ //第一层循环,从对角线向右上方向推进
for(i = 1;i <= n - l; i++) //第二层循环,从左上至右下填写
{
j = i + l -1;
M[i][j] = 999999; //将此值初始为很大的值,便于下面的比较
for(k = i ;k <= j -1; k++){ //第三层循环,列出所有有可能作为cut点的情况并计算结果
q = M[i][k] + M[k+1][j] + A[i-1] * A[k] * A[j];
if (q < M[i][j]){
M[i][j] = q; //再一次次比较中找出最小次数的情况
S[i][j] = k; //并将cut点存入S表作以记录
}

}
}

}

return 0;
}
int PrintMatrix(int **S,int i, int j) //依据S表打印结果,思想为递归
{
if (i == j){
printf("A%d",i);
}
else{
printf("(");
PrintMatrix(S,i,S[i][j]);
PrintMatrix(S,S[i][j] + 1,j);
printf(")");
}
return 0;
}

int main()
{
int A[] = {30,35,15,5,10,20,25};
int i,j;
int n = sizeof(A) / sizeof(int) ;
int **M = (int **)malloc(sizeof(int *) * n); //申请二维数组
for(i = 0; i < n; i++){
M[i] = (int*)malloc(sizeof(int) * n);
}
int **S = (int **)malloc(sizeof(int *) * n);
for(i = 0; i < n; i++){
S[i] = (int*)malloc(sizeof(int) * n);
}


printf("\n");
Matrix(A,M,S,n); //调用函数

for ( i = 0; i < n; i++ ){ //打印M表
for( j = 0; j < n; j++ ){
printf("%5d ",M[i][j]);
}
printf("\n");
}

printf("==============================================================\n");

for ( i = 0; i < n; i++ ){ //打印S表
for( j = 0; j < n; j++ ){
printf("%5d",S[i][j]);
}
printf("\n");
}
PrintMatrix(S,1,6); //显示结果
printf("\n");
return 0;
}

实验结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  0     0     0     0     0     0     0 
0 0 15750 7875 9375 11875 15125
0 0 0 2625 4375 7125 10500
0 0 0 0 750 2500 5375
0 0 0 0 0 1000 3500
0 0 0 0 0 0 5000
0 0 0 0 0 0 0
==============================================================
0 0 0 0 0 0 0
0 0 1 1 3 3 3
0 0 0 2 3 3 3
0 0 0 0 3 3 3
0 0 0 0 0 4 5
0 0 0 0 0 0 5
0 0 0 0 0 0 0
((A1(A2A3))((A4A5)A6))

感谢阅读,欢迎指正。

-------------本文结束感谢您的阅读-------------