一、斐波那契数列(递归VS动态规划)

1、斐波那契数列——递归实现(python语言)——自顶向下

递归调用是非常耗费内存的,程序虽然简洁可是算法复杂度为O(2^n),当n很大时,程序运行很慢,甚至内存爆满。

  1. def fib(n):
  2. #终止条件,也就是递归出口
  3. if n == 0 or n == 1:
  4. return 1
  5. else:
  6. #递归条件
  7. return (fib(n-1) + fib(n - 2))

2、斐波那契数列——动态规划实现(python语言)——自底向上

动态规划——将需要重复计算的问题保存起来,不需要下次重新计算。对于斐波那契数列,算法复杂度为O(n)。

  1. def dp_fib(n):
  2. #初始化一个数组,用于存储记录计算的结果。
  3. res = [None] * (n + 1)
  4. #前两项设置为1。
  5. res[0] = res[1] = 1
  6. #自底向上,将计算结果存入数组内。
  7. for i in range(2, (n + 1)):
  8. res[i] = res[i-1] + res[i-2]
  9. return res[n]

3、方法概要

  (1)构造一个公式,它表示一个问题的解是与它的子问题的解相关的公式:

     

  (2)为这些子问题做索引,以便于它们能够在表中更好的存储与检索(用数组存储)。

  (3)以自底向上的方法来填写这个表格;首先填写最小的子问题的解。

  (4)这就保证了当我们解决一个特殊的子问题时,可以利用比它更小的所有可利用的子问题的解。

总之,因为在上世纪40年代(计算机普及很少时),这些规划设计是与“列表”方法相关的,因此被称为动态规划——Dynamic Programing。

二、动态规划算法——思想简介

1、DP算法思想

  (1)将待求解的问题分解称若干个子问题,并存储子问题的解而避免计算重复的子问题,并由子问题的解得到原问题的解。

   (2)动态规划算法通常用于求解具有某种最有性质的问题。

   (3)动态规划算法的基本要素:最优子结构性质和重叠子问题。

      最优子结构性质:问题的最优解包含着它的子问题的最优解。即不管前面的策略如何,此后的决策必须是基于当前状态(由上一次的决策产生)的最优决策。

      重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些问题被反复计算多次。对每个子问题只解一次,然后将其解保存起来,

            以后再遇到同样的问题时就可以直接引用,不必重新求解。

2、DP算法——解决问题的基本特征

  (1)动态规划一般求解最值(最优、最大、最小、最长)问题;

   (2)动态规划解决 的问题一般是离散的,可以分解的(划分阶段的)。

   (3)动态规划结局的问题必须包含最优子结构,即可以有(n-1)的最优推导出n的最优。

3、DP算法——解决问题的基本步骤

  动态规划算法的四个步骤:

    (1)刻画最优解的结构特性。(一维、二维、三维数组);

    (2)递归的定义最优解。(状态转移方程)

    (3)以自底向上的方法来计算最优解。

    (4)从计算得到的解来构造一个最优解。

 4、求解例子——求阶乘 n!

  1. #递归实现求阶乘
  2. def multiply(n):
  3. if n == 0 or n == 1:
  4. return 1
  5. return n * multiply(n -1)
  6.  
  7. #动态规划实现求阶乘
  8. def dp_multiply(n):
  9. temp = [None] * (n + 1)
  10. temp[0] = 1
  11. temp[1] = 1
  12. for i in range(2, n + 1):
  13. temp[i] = i * temp[i - 1]
  14. return temp[n]

 三、动态规划——常见例题

1、求解最长不降子序列

  (1)方法一:普通方法,算法复杂度为O(n^2)。

      假设原始的数列为数组 a

      分析:

        刻画结构特性:用F[ i ] 表示前 i 项最长不下降子序列的长度;

        状态转移方程:如果a [ i ] >=a [ j ],  F[i] = max(F[i], F[j] + 1)  其中,0 <= j < i

        数据存储:自底向上求解最小子结构最优解存入数组

其中,pre[ i ]表示以元素a [ i ] 为结尾的最长不降序列的前一个元素索引(也就是以a[i]结尾的最长不降序列的倒数第二个元素)。存储这个值是为了方便输出最长的不降序列。

  1. def Longest_Increaseing(a):
  2. F = [1] * len(a)
  3. pre = [0] * len(a)
  4. for i in range(1, len(a)):
  5. for j in range(i):
  6. if a[i] >= a[j]:
  7. F[i] = max(F[i], F[j] + 1)
  8. pre[i] = j
  9. return F, pre
  10. a = [5,2,8,6,3,6,9,7]
  11. F, pre = Longest_Increaseing(a)
  12.  
  13. #这里只是能获得两个数组,其中F[i]的最大值就是最长不降序列的长度。

接下来,输出最长的不降序列的元素值,请看下面的代码:

  1. def Longest_Increaseing(a):
  2. F = [1] * len(a)
  3. pre = [0] * len(a)
  4. for i in range(1, len(a)):
  5. for j in range(i):
  6. if a[i] >= a[j]:
  7. F[i] = max(F[i], F[j] + 1)
  8. pre[i] = j
  9. return F, pre
  10. a = [5,2,8,6,3,6,9,7]
  11. F, pre = Longest_Increaseing(a)
  12.  
  13. #最长序列的索引
  14. k = F.index(max(F))
  15. #输出序列的列表
  16. result = [None] * F[k]
  17. flag = True
  18. Len = F[k]
  19. while flag:
  20. result[Len - 1] = a[k]
  21. k = pre[k]
  22. if k == 0:
  23. flag = False
  24. Len -= 1
  25. print(result)
  26.  
  27. #输出结果:[2, 3, 6, 9]
  28.  

  (2)方法二:时间复杂度为O(n * log(n))

    参考博文:最长不下降子序列 NlogN && 输出序列       https://www.cnblogs.com/milky-w/p/8431333.html

2、求解最长的公共子序列

求解最长公共子序列代码如下(python语言):

  1. import numpy as np
  2. def LCS(str1, str2):
  3. #获取两个序列的长度
  4. m = len(str1)
  5. n = len(str2)
  6. #生成一个存储计算子问题的二位矩阵,并将元素初始化为0。
  7. #这个矩阵的尺寸比两个序列的尺寸分别大1个单位。
  8. #对于这个矩阵,第一行和第一列元素值必然为0。
  9. #C[i][j]的含义是:Xi = (x1, x2, x3,..., xi)和Yj = (y1, y2, x3,..., yj)的最长公共子序列
  10. C = np.zeros((m+1, n+1), dtype=int)
  11. b = np.zeros((m+1, n+1), dtype=int)
  12.  
  13. for i in range(1, m+1):
  14. for j in range(1, n+1):
  15. #请注意这里为什么是i-1和j-1,因为其实C[1][1]表示的是
  16. # 两个序列的首个元素的最长公共子序列,对应的是str1[0]和str2[0]
  17. if str1[i-1] == str2[j-1]:
  18. C[i][j] = C[i-1][j-1] + 1
  19. b[i][j] = 1 #表示对角线方向
  20. else:
  21. if C[i][j-1] <= C[i-1][j]:
  22. b[i][j] = 2 #表示朝上方向
  23. else:
  24. b[i][j] = 3 #表示朝左方向
  25. C[i][j] = max(C[i][j-1], C[i-1][j])
  26. return C, b
  27.  
  28. test1 = ['b', 'd','c', 'a', 'b', 'a']
  29. test2 = ["a","b","c","b","d","a","b"]
  30. a, b = LCS(test2, test1)
  31.  
  32. print(a)
    #矩阵a存储的是公共子序列的长度,最大值就是最大公共子序列的长度

[[0 0 0 0 0 0 0]
[0 0 0 0 1 1 1]
[0 1 1 1 1 2 2]
[0 1 1 2 2 2 2]
[0 1 1 2 2 3 3]
[0 1 2 2 2 3 3]
[0 1 2 2 3 3 4]
[0 1 2 2 3 4 4]]

  1. print(b)
    #这里: 1表示对角线方向、2表示朝上、3表示朝左,主要是为了求具体的子序列用的。

[[0 0 0 0 0 0 0]
[0 2 2 2 1 3 1]
[0 1 3 3 2 1 3]
[0 2 2 1 3 2 2]
[0 1 2 2 2 1 3]
[0 2 1 2 2 2 2]
[0 2 2 2 1 2 1]
[0 1 2 2 2 1 2]]

接下来是输出最长公共子序列:

  1. import numpy as np
  2. def LCS(str1, str2):
  3. #获取两个序列的长度
  4. m = len(str1)
  5. n = len(str2)
  6. #生成一个存储计算子问题的二位矩阵,并将元素初始化为0。
  7. #这个矩阵的尺寸比两个序列的尺寸分别大1个单位。
  8. #对于这个矩阵,第一行和第一列元素值必然为0。
  9. #C[i][j]的含义是:Xi = (x1, x2, x3,..., xi)和Yj = (y1, y2, x3,..., yj)的最长公共子序列
  10. C = np.zeros((m+1, n+1), dtype=int)
  11. b = np.zeros((m+1, n+1), dtype=int)
  12.  
  13. for i in range(1, m+1):
  14. for j in range(1, n+1):
  15. #请注意这里为什么是i-1和j-1,因为其实C[1][1]表示的是
  16. # 两个序列的首个元素的最长公共子序列,对应的是str1[0]和str2[0]
  17. if str1[i-1] == str2[j-1]:
  18. C[i][j] = C[i-1][j-1] + 1
  19. b[i][j] = 1 #表示对角线方向
  20. else:
  21. if C[i][j-1] <= C[i-1][j]:
  22. b[i][j] = 2 #表示朝上方向
  23. else:
  24. b[i][j] = 3 #表示朝左方向
  25. C[i][j] = max(C[i][j-1], C[i-1][j])
  26. return C, b
  27.  
  28. def Print_Lcs(b, X, i , j):
  29. if i == 0 or j == 0:
  30. return
  31. if b[i][j] == 1:
  32. Print_Lcs(b, X, i-1, j-1)
  33. print(X[i-1]) #为什么是i-1,因为b矩阵的行比X的行长一个单位,而且只输出相等的值,表示公共元素。
  34. elif b[i][j] == 2:
  35. Print_Lcs(b, X, i-1, j)
  36. else:
  37. Print_Lcs(b, X, i, j-1)
  38.  
  39. if __name__ == '__main__':
  40. test1 = ['b', 'd','c', 'a', 'b', 'a']
  41. test2 = ["a","b","c","b","d","a","b"]
  42. a, b = LCS(test2, test1)
  43. Print_Lcs(b, test2, 7, 6)
  44.  
  45. #输出的结果是: b、c、b、a 。(请注意这里结果不唯一,因为最长子序列长度为4, 存在三个序列长度为4的子序列)

动态规划——DP算法(Dynamic Programing)的更多相关文章

  1. c++动态规划dp算法题

    问题1:找硬币,换钱的方法 输入: penny数组代表所有货币的面值,正数不重复 aim小于等于1000,代表要找的钱 输出:换钱的方法总数 解法1:经典dp,空间复杂度O(n*aim) class ...

  2. Siimple DP (Dynamic Programing)

    HDU 2084:https://vjudge.net/problem/HDU-2084 Problem Describe : When it comes to the DP algorithm, a ...

  3. 算法-动态规划DP小记

    算法-动态规划DP小记 动态规划算法是一种比较灵活的算法,针对具体的问题要具体分析,其宗旨就是要找出要解决问题的状态,然后逆向转化为求解子问题,最终回到已知的初始态,然后再顺序累计各个子问题的解从而得 ...

  4. 0-1背包的动态规划算法,部分背包的贪心算法和DP算法------算法导论

    一.问题描述 0-1背包问题,部分背包问题.分别实现0-1背包的DP算法,部分背包的贪心算法和DP算法. 二.算法原理 (1)0-1背包的DP算法 0-1背包问题:有n件物品和一个容量为W的背包.第i ...

  5. 最大子段和的DP算法设计及其效率测试

    表情包形象取自番剧<猫咪日常> 那我也整一个 曾几何时,笔者是个对算法这个概念漠不关心的人,由衷地感觉它就是一种和奥数一样华而不实的存在,即便不使用任何算法的思想我一样能写出能跑的程序 直 ...

  6. 动态规划dp

    一.概念:动态规划dp:是一种分阶段求解决策问题的数学思想. 总结起来就一句话:大事化小,小事化了 二.例子 1.走台阶问题 F(10):10级台阶的走法数量 所以:F(10)=F(9)+F(8) F ...

  7. 华为笔试——C++平安果dp算法

    题目:平安果 题目介绍:给出一个m*n的格子,每个格子里有一定数量的平安果,现在要求从左上角顶点(1,1)出发,每次走一格并拿走那一格的所有平安果,且只能向下或向右前进,最终到达右下角顶点(m,n), ...

  8. C++数字三角形问题与dp算法

    题目:数字三角形 题目介绍:如图所示的数字三角形,要求从最上方顶点开始一步一步下到最底层,每一步必须下一层,求出所经过的数字的最大和. 输入:第一行值n,代表n行数值:后面的n行数据代表每一行的数字. ...

  9. dp算法之硬币找零问题

    题目:硬币找零 题目介绍:现在有面值1.3.5元三种硬币无限个,问组成n元的硬币的最小数目? 分析:现在假设n=10,画出状态分布图: 硬币编号 硬币面值p 1 1 2 3 3 5 编号i/n总数j ...

随机推荐

  1. vue中获取节点.

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  2. windows下 Mysql 8.0.x 数据库简单的导出和导入!!!

    1.首先需要进入到mysql安装目录下的bin目录,执行cmd进入命令窗口. 2.导出(导出某个数据库,也可以针对某张表导出)2.1导出数据结构以及数据的命令: mysqldump -u root - ...

  3. 廖雪峰Java16函数式编程-2Stream-6reduce

    1. 聚合方法 Stream.reduce()是一个Stream的聚合方法:把一个Stream的所有元素聚合成一个结果 例如: Stream.of(1, 2, 3, 4, 5).count(); // ...

  4. Linux的命令提示符 修改

    Linux的命令提示符可按个人喜好随意更改,修改PS1的值即可: 在Ubuntu下若只是个别用户下修改~/.profile文件就好,所有用户统一则修改/etc/profile: 加入: export ...

  5. Java基本语法.part02

    变量 变量的概念: 内存中的一个存储区域 该区域有自己的名称(变量名)和类型(数据类型) Java中每个变量必须先声明,后使用 该区域的数据可以在同一类型范围内不断变化 使用变量注意: 变量的作用域: ...

  6. CF377D Developing Game

    题目链接: luogu 题目分析: 把每个人当成一个三元组\([l_i, r_i, v_i]\) 考虑每个人对哪个能力区间\([L, R]\)有贡献 应该是左端点在\([l_i, v_i]\),右端点 ...

  7. CSS 继承和优先级

    CSS继承性 CSS属性继承:外层元素的样式,会被内层元素进行继承. 多个外层元素的样式,最终都会“叠加”到内层元素上. 什么样的CSS属性能被继承呢? CSS文本属性都会被继承的: color. f ...

  8. CCPC 2019 网络赛 1002 array (权值线段树)

    HDU 6703 array   题意:   给定一个数组 \(a_1,a_2, a_3,...a_n\) ,满足 \(1 \le a[i]\le n\) 且 \(a[i]\) 互不相同.   有两种 ...

  9. ztree 树状图——例

    效果: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta ...

  10. C++: Type conversions

    1.static_cast     static_cast可以转换相关联的类,可以从子类转换成父类.也能从父类转向子类,但是如果转换的父类指针(或者父类引用)所指向的对象是完整的,那么是没有问题:但是 ...