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

动态规划算法的基本要素

(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
81

#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))

感谢阅读,欢迎指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值