[C++] 动态规划之矩阵连乘、最长公共子序列、最大子段和、最长单调递增子序列

一、动态规划的基本思想

  动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。

  将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。为了达到此目的,我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。

二、动态规划的基本要素(特征)

  1、最优子结构:

  当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。

  2、重叠子问题:

 在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题的解。

三、用动态规划求解问题的主要步骤

  1、找出最优解的性质,并刻画其结构特征;

  2、递归地定义最优值(写出动态规划方程);

  3、以自底向上的方式计算出最优值;

  4、根据计算最优值时得到的信息,构造一个最优解。

  说明:(1)步骤 1~3 是动态规划算法的基本步骤;
     (2)在只需要求出最优值的情形,步骤 4 可以省略;
     (3)若需要求出问题的一个最优解,则必须执行步骤  4。

四、动态规划实例

  1、矩阵连乘问题

  m × n 矩阵 A 与 n × p 矩阵 B 相乘需耗费的时间。我们把 m x n x p 作为两个矩阵相乘所需时间的测量值。

  现在假定要计算三个矩阵 A、B  和 C 的乘积,有两种方式计算此乘积。

  (1)先用 A 乘以 B 得到矩阵 D,然后 D 乘以 C 得到最终结果,这种乘法的顺序为(AB)C;

  (2)先用 B 乘以 C 得到矩阵 E,然后 E 乘以 A 得到最终结果,这种乘法的顺序为 A(BC)。

  尽管这两种不同的计算顺序所得的结果相同,但时间消耗会有很大的差距。

  实例:

图1.1 A、B 和 C 矩阵

  矩阵乘法符合结合律,所以在计算 ABC 矩阵连乘时,有两种方案,即 (AB)C  和 A(BC)。

  对于第一方案(AB)C  和,计算:

图1.2 AB 矩阵相乘

  其乘法运算次数为:2 × 3 × 2 = 12

图1.3 (AB)C 矩阵连乘

  

  其乘法运算次数为:2 × 2 × 4 = 16
  总计算量为:12 + 16 = 28 

  对第二方案 A(BC),计算:

图1.4 BC 矩阵相乘

  其乘法运算次数为:3 × 2 × 4 = 24

图1.5  A、B 和 C  矩阵连乘

  其乘法运算次数为:2 × 3 × 4 = 24

  总计算量为:24 + 24 = 48

  可见,不同方案的乘法运算量可能相差很悬殊。

  问题定义:

  给定 n 个矩阵 {A1, A2, …, An},其中 A与 Ai+1 是可乘的,i = 1,2,…,n-1。考察这 n 个矩阵的连乘积 A1A2…An。 由于矩阵乘法满足结合律,所以计算矩阵的连乘可以有许多不同的计算次序。

  这种计算次序可以用加括号的方式来确定。完全加括号的矩阵连乘积可递归地定义为:

  (1)单个矩阵是完全加括号的;

  (2)矩阵连乘积 A 是完全加括号的,则 A 可表示为 2 个完全加括号的矩阵连乘积 B 和 C 的乘积并加括号,即 A = (BC)。设有四个矩阵 A, B, C, D,总共有五种完全加括号的方式:   (A((BC)D))  ,  (A(B(CD)))  ,  ((AB)(CD))  ,  (((AB)C)D)  ,  ((A(BC)D))。

  a. 找出最优解的性质,并刻画其结构特征;

  将矩阵连乘积 AiAi+1…Aj ,简记为 A[i : j], 这里 i≤j;考察计算 A[1:n] 的最优计算次序。

  设这个计算次序在矩阵 Ak 和 Ak+1 之间将矩阵链断开,1 ≤ k < n,则其相应完全加括号方式为 (A1A2…Ak)(Ak+1Ak+2…An)。

  总计算量 = A[1:k] 的计算量 +  A[k+1:n]的计算量 + A[1:k]和A[k+1:n]相乘的计算量

  特征:计算 A[1:n] 的最优次序所包含的计算矩阵子链 A[1:k] 和 A[k+1:n] 的次序也是最优的。

  b. 递归地定义最优值(写出动态规划方程);

图1.6 建立递归关系

  c. 以自底向上的方式计算出最优值。 

 1 #include <iostream>
 2 using namespace std;
 3
 4 #define NUM 51
 5 int p[NUM];            //矩阵维数 P0 x P1,P1 x P2,P2 x P3,...,P5 x P6
 6 int m[NUM][NUM];    //最少乘次数 / 最优值数组
 7 int s[NUM][NUM];    //最优断开位置
 8
 9 void MatrixChain(int n)
10 {
11     for (int i = 1; i <= n; i++) m[i][i] = 0;
12
13     for (int r = 2; r <= n; r++)                    //矩阵个数
14         for (int i = 1; i <= n - r+1; i++)             //起始
15         {
16             int j=i+r-1;                            //结尾
17             m[i][j] = m[i+1][j]+ p[i-1]*p[i]*p[j];    //计算初值,从i处断开,计算最优断开位置
18             s[i][j] = i;
19             for (int k = i+1; k < j; k++)
20             {
21                 int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
22                 if (t < m[i][j]) { m[i][j] = t; s[i][j] = k;}
23             }
24         }
25 }
26
27 void TraceBack(int i, int j)
28 {
29     if(i==j)
30         cout<<"A"<<i;
31     else
32     {
33         cout<<"(";
34         TraceBack(i,s[i][j]);
35         TraceBack(s[i][j]+1,j);
36         cout<<")";
37     }
38 }
39
40 int main()
41 {
42     int n;
43     scanf("%d", &n);
44     int i, temp;
45     for (i=0; i<n; i++)
46         scanf("%d%d", &p[i], &temp);
47     p[n] = temp;
48     MatrixChain(n);
49     printf("%d\n", m[1][n]);
50     TraceBack(1, n);
51     return 0;
52 }

  

时间: 2024-10-14 19:03:53

[C++] 动态规划之矩阵连乘、最长公共子序列、最大子段和、最长单调递增子序列的相关文章

最长单调递增子序列——动态规划

题目描述: 给定一个序列X[0···n],找出它的最长的单调递增子序列(Longest Increasing Subsequence) 解题思路: 使用动态规划方法. 对于i= 1, 2, --,n,考虑以xi作为最后项的最长递增子序列的长度C[i]. 如果在xi项前面存在xj < xi , 那么 C[i] = max{C[j]} +1:否则,C[i] = 1. 因此, C[i] = max{C[j]} + 1, 存在j,1<=j<i, xj<xi C[i] = 1, 所有j,1&

利用动态规划找出最长公共单调递增子序列

1.设计一个O(n2)时间的算法. 方法如下: ①利用快速排序先将原序列排序. ②然后再计算原序列和已排序序列两者公共子序列. ③打印公共子序列. 代码如下: /************************************************************************/ /* 算法导论15.4-5 * 找出n个数的序列中最长的单调递增子序列 * 时间复杂度为O(n^2)*/ /*****************************************

nyist oj 214 单调递增子序列(二) (动态规划经典)

单调递增子序列(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 给定一整型数列{a1,a2...,an}(0<n<=100000),找出单调递增最长子序列,并求出其长度. 如:1 9 10 5 11 2 13的最长单调递增子序列是1 9 10 11 13,长度为5. 输入 有多组测试数据(<=7) 每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的下一行里有n个整数,表示数列中的所有元素.每个整形数中间用空格间隔开(0<n<=1

最长单调递增子序列 POJ 3903 Stock Exchange .

题目传送门 : -------->点这里点这里<---------- 题目大意: 给出一串正整数,个数为N个.1<=N<=100000.求最长单调递增子序列长度. 样例输入: 6 5 2 1 4 5 3 3 1 1 1 4 4 3 2 1 样例输出: 3 1 1 =================================================================== 最长单调递增子序列问题的DP朴素算法复杂度为 O(n^2); 然而题目中的 N 最大有

最长单调递增子序列的三种解法

问题描述: 找出由n个数组成的序列的最长单调递增子序列 解法一:转化成LCS问题求解,时间复杂度为O(n*n). 思路:原序列为A,把A按升序排序得到序列B,求出A,B序列的最长公共子序列,即为A的最长单调递增子序列. #include<iostream> #include<algorithm> #include<string> #include<cstdio> using namespace std; //转化成LCS问题,时间复杂度O(n*n) int

hdoj 5087 Revenge of LIS II 【第二长单调递增子序列】

题目:hdoj 5087 Revenge of LIS II 题意:很简单,给你一个序列,让你求第二长单调递增子序列. 分析:其实很简单,不知道比赛的时候为什么那么多了判掉了. 我们用O(n^2)的时间求单调递增子序列的时候,里面在加一层循环维护sum数组,表示前面有几个可以转移当当前,求前面sum的和保存到当前. 最后求最后一个sum[n-1]是否为1就ok,为1的话在最长的基础上减一,否则就是最长的. AC代码: #include <iostream> #include <algor

最长单调递增子序列问题

题目:设计一个 O( n ^ 2 )复杂度的算法,找出由 n 个数组成的序列的最长单调递增子序列. import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); int[] nums = new

求最长单调递增子序列

//求最长单调递增子序列 #include<stdio.h> #define MAXN 20 void disp(int a[],int b[],int k){ int i; for(i=k-1;i>0;i--){ if(b[k] == b[i]+1 && a[i] <= a[k]){ disp(a,b,i); break; } } printf("%d ",a[k]); } int maxL(int b[],int n){ //求数组b中最大值

最长单调递增子序列-LIS问题

http://zju.acmclub.com/index.php?app=problem_title&id=1&problem_id=1911 最长单减子序列.最长单增子序列.相继元素之间满足某种条件(例如绝对值之差不超过d)的最长子序列等,都是一个类型的动态规划. 下面给出一个n平方级别的基本算法. 思路:定义dp[i]代表A[i:n]中,以A[i]为开头的最长单增序列的长度. 从A[i-1]开始,最长单调递增序列应该按下列方法求出: 在A[i],A[i+1],A[i+2],-,A[n]

[dp]最长单调递增子序列

https://www.51nod.com/tutorial/course.html#!courseId=12 解题关键: 如果将子序列按照长度由短到长排列,将他们的最大元素放在一起,形成新序列B{b1,b2,……bj},则序列B满足b1 < b2 < …… <bj.这个关系比较容易说明,假设bxy表示序列A中长度为x的递增序列中的第y个元素,显然,如果在序列B中存在元素bmm > bnn,且m < n则说明子序列Bn的最大元素小于Bm的最大元素,因为序列是严格递增的,所以在