一周编程集训day5:递归及DP

一周编程集训day5:递归及DP

1 任务

递归及DP:学习递归思想,动态规划思想,并同时温习前四天内容,做出总结!

2 概念介绍

  1. 递归:在调用一个函数的过程中,直接间接地调用了函数本身这个就叫递归。简而言之就是自己调用自己。大概步骤如下:
    1、写出临界条件
    2、找出这一次和上一次关系
    3、假设当前函数已经能用,调用自身计算上一次的结果,再求出本次的结果
    递归两个基本要素:
    (1) 边界条件:确定递归到何时终止,也称为递归出口。
    (2) 递归模式:大问题是如何分解为小问题的,也称为递归体。
    一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
    递归算法一般用于解决三类问题
    (1)数据的定义是按递归定义的。(Fibonacci函数)
    (2)问题解法按递归算法实现。(回溯)
    (3)数据的结构形式是按递归定义的。(树的遍历,图的搜索)
    递归的缺点: 递归算法解题的运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。

  2. 动态规划(DP):Dynamic Programming(这里的“Programming”为“规划”,而非指“程序”、“编程”),研究多步决策过程最优化问题的一种数学方法,英文缩写DP。在动态规划中,为了寻找一个问题的最优解(即最优决策过程),将整个问题划分成若干个相应的阶段,并在每个阶段都根据先前所作出的决策作出当前阶段最优决策,进而得出整个问题的最优解
    当求解的问题满足以下两个条件时, 就应该使用动态规划:
    1.主问题的答案 包含了 可分解的子问题答案 (也就是说,问题可以被递归的思想求解)
    2.递归求解时, 很多子问题的答案会被多次重复利用
    可解决问题: 最长公共子序列,最长公共子串,最长递增子序列,最长回文子串,硬币的组合数,硬币的最少组合方法,最小编辑距离,背包问题等。
    动态规划的方法主要有两个
    1)直接自顶向下实现递归式,并将中间结果保存,这叫备忘录法
    创建了一个n+1大小的数组来保存求出的斐波拉契数列中的每一个值,在递归的时候如果发现前面fib(n)的值计算出来了就不再计算,如果未计算出来,则计算出来后保存在Memo数组中,下次在调用fib(n)的时候就不会重新递归了。
    2)按照递归式自底向上地迭代,将结果保存在某个数据结构中求解。
    1.先计算子问题,再由子问题计算父问题。
    2.当前问题可以拆分为多个子问题,并且依赖于这些子问题,那么我们称为此问题符合子结构,而若当前状态可以由某个阶段的某个或某些状态直接得到,那么就符合最优子结构。
    例题1 背包问题(01背包,完全背包,多重背包)
    首先分别解释一下三种背包的含义:

  • 01背包:有n种物品与承重为m的背包。每种物品只有一件,每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大
  • 完全背包:有n种物品与承重为m的背包。每种物品有无限件,每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大
  • 多重背包:有n种物品与承重为m的背包。每种物品有有限件num[i],每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大
    思路:遇到这种问题,最简单也是最耗费时间的解决方法是遍历,将所有情况都计算出来,再从结果中找最优解,我们今天介绍的动态规划就是在这个基础上演变而来的。我们都知道,遍历之所以耗费时间,是由于它的计算量巨大,我们通过观察它的计算过程可以发现,其实它的很多计算操作计算的都是之前计算过的量。那我们如果把这些已经计算过的量储存起来,当我们需要的适合直接提取它的结果,就起到了减少计算量的效果,我们的动态规划使用的就是这种思想。
#-*- coding: utf-8 -*-
def max(m,n):
   if(m>n):
       return m
   if(m<n):
       return n
def max1(m,n):
   if(m<n):
       return true
   if(m>n):
       return false
def best(m,list1,list2,n):
   r=[[0 for i in range(n)] for i in range(m+1)];
   for i in range(m+1):
       for j in range(n):
           r[i][j]=0
   for i in range(1,m+1):
       for j in range(1,n):
           r[i][j] = r[i][j-1]
           if(i>=list1[j]):
               r[i][j]=max(r[i][j-1],r[i-list1[j]][j-1]+list2[j])
   return r[m][n-1];
if __name__ =="__main__":
   m= 0;
   print"请输入最大重量"
   m = input()
   print"请输入一组物品的总数量"
   n=input()
   n=n+1
   list1 = [0 for i in range(n)]
   list2 = [0 for i in range(n)]
   print"请依次输入物品的质量"
   for i in range(1, n):
       list1[i] = int(input())
   print"请依次输入物品的价值"
   for i in range(1, n):
       list2[i] = int(input())
       list3[i]=0
   q=best(m,list1,list2,n)
   print q

这段代码最重要的部分就是best函数,我们来一行一行分析一下。
首先它创建了一个m+1行n列的数组,用于存储我们已经计算过的数据,r[m][n-1]的意思就是当背包最大重量为m、物品为前n个时,能装下的物品总价格最高的值。(要注意到我们在主函数输入n之后将n加1一次,这么做是为了方便计算,把n加1后可以留出来n=0的位置表示没有物品时的最优解的值)
接下来我们把r数组初始化为0,然后,运用动态规划的基本思想,先限制一个背包的最大重量,再挨个的增加物品数量,求它的最大值,为了理解方便,我们通过这个图来解释。
在这里插入图片描述
我们现在有五个物品abcde,weight是他们的重量,value是他们的价格,浅蓝色的是我们限制的最大重量。那么这张图是怎么得来的呢?
以当最大重量为6时,a行的12的计算方法为例。这就关系到我们代码中最关键的一行:

    r[i][j]=max(r[i][j-1],r[i-list1[j]][j-1]+list2[j]) 

其中r[i][j]是我们要计算的结果,我们要通过比较r[i][j-1]与r[i-list1[j]][j-1]+list2[j]的值来判断是否把a放进去,此时i=6,j=5(这个图是从下往上生成的,与我们的代码循环方式相反)r[i][j-1]的值可以从表上得到为9,,r[i-list1[j]][j-1]+list2[j]的意思为把a所占的重量出去后bcde在总重量减去a重量的限制中能达到的最优解,减去a后总重量为4,我们可以在b那一行,浅蓝色栏为4的表格中找到此时最优解为6,之后再加上a的价格,就得出了12,再最后与9比较,12大于9,所以12就是我们要的最优解。整个算法就是这样一步一步计算过来的。

3 参考

  1. 递归及DP
  2. 背包问题
  3. 递归及DP2
  4. python什么是递归
  5. 动态规划(DP)的整理-Python描述
发布了48 篇原创文章 · 获赞 49 · 访问量 9230

猜你喜欢

转载自blog.csdn.net/qq_30006749/article/details/86704857