[算法]动态规范(dp)

动态规范总体思想

  1. 基于 斐波那契数列

f i b ( n ) = { 1 n = 1 或 n = 2 f i b ( n − 1 ) + f i b ( n − 2 ) o / w fib(n)=\begin{cases} 1 & n=1 或 n=2 \\ fib(n-1) + fib(n-2) & o/w \\ \end{cases} fib(n)={1fib(n1)+fib(n2)n=1n=2o/w

  1. 动态规划解决来递归的重复子问题,递归空间时间复杂度 O ( n 2 ) O(n^2) O(n2) ,动态规划 O ( n ) O(n) O(n)
    • 比如说 下面的 例题二 中通过数组保存过程状态,需要用的时候直接获取到,不需要在算一遍

在这里插入图片描述

做题思路

  • 选择第 i 个数字
  • 不选 第 i 个数字
  • 找出口

例题一

在数组中找出数字和最大的数,其中两个数不能是相邻的两个数

示例

arr = [1,2,4,1,7,8,3]
返回最大数为 15 [1,4,8]

方法一 动态规划

解题思路

  • 如图 思路分析
  1. 先为数组添加下标 方便理解
  2. 根据条件可知 只能找不相邻的两个数
  3. 假设 opt[i] 表示到下标为 i 的最佳方案
  4. 选择下标为 i 的方案 opt[i-2] + arr[i]
  5. 不选择下标为 i 的方案 opt[i]
  6. 在比较 这两个的大小
  7. 找出口
    1.当数组 arr 只有一个数字的时候 最大数 opt[0] = arr[0]
    2.当数组 arr 有两个数字的时候 最大数 opt[1] = max(arr[0],arr[1])
  • 根据思路可推到出公式

o p t ( 0 ) = { a r r [ 0 ] i = 0 opt(0)=\begin{cases} arr[0] & i = 0 \\ \end{cases} opt(0)={arr[0]i=0

o p t ( i ) = m a x = { o p t [ i − 2 ] + a r r [ i ] 选 择 o p t [ i − 1 ] 不 选 opt(i)=max=\begin{cases} opt[i-2]+ arr[i] & 选择 \\ opt[i-1] & 不选 \\ \end{cases} opt(i)=max={opt[i2]+arr[i]opt[i1]

在这里插入图片描述

在这里插入图片描述

动态规划写法
def dp_opt(arr):
    opt = np.zeros(len(arr))
    opt[0] = arr[0]
    opt[1] = max(arr[0],arr[1])
    for i in range(2,len(arr)):
        a = opt[i-2] + arr[i]#选择arr[i]
        b = opt[i-1]
        opt[i] = max(a,b)
    return opt[len(arr)-1]
递归写法
  • 需要把 arr 传递进去
def rec_opt(arr,i):
    # 找出口
    if i ==0:
        return arr[0]
    elif i==1:
        return max(arr[0],arr[1])
    else:
        a = rec_opt(arr,i-2) + arr[i] #选择arr[i]
        b = rec_opt(arr,i-1)# 不选arr[i]
        return max(a,b)

例题二

在数组中找出和为s的数,有则返回 True,没有则返回 False

示例

arr = [3,34,4,12,5,2]
k = 9
结果 为True (4+5=9)

方法一 动态规划

解题思路

  • 如图 思路分析
  1. 先为数组添加下标 方便理解
  2. 根据条件可知 需要找出和为 s 的数
  3. 我们假设 subset(i,s) i 表示数组下标 s 表示需要求的数
  4. opt[i] 表示到下标为 i 的最佳方案
  5. 选择下标为 i 的方案 那么剩下的数组需要 subset(arr[i-1],s-arr[i])
  6. 不选择下标为 i 的方案 那么剩下的数组需要 subset[i-1] + arr[i]
  7. 在比较 这两个的大小
  8. 找出口
    1.当 s = 0 表示 i 之前的数字加起来已经等于 s 了 则直接返回 True
    2.当 i = 0 的时候,表示 arr[0] == s 才能返回为 True
    3.当左边的数字比右边的 s 还有大,则(s-arr[i]<0) ,所以只能选择第二种情况,subset[i-1] + arr[i]
  • 根据思路可推到出公式

s u b s e t ( i , s ) = { r e t u r n T r u e s = = 0 a r r [ 0 ] = = s i = = 0 s u b s e t ( i − 1 , s ) a r r [ i ] > s s u b s e t ( i − 1 , s − a r r [ i ] ) a r r [ i ] < s 且 选 择 i s u b s e t ( i − 1 , s ) a r r [ i ] < s 不 选 择 i subset(i,s)=\begin{cases} return True & s==0 \\ arr[0]==s & i==0 \\ subset(i-1,s) & arr[i]>s \\ subset(i-1,s-arr[i]) & arr[i]<s 且 选择 i \\ subset(i-1,s) & arr[i]<s 不选择 i\\ \end{cases} subset(i,s)=returnTruearr[0]==ssubset(i1,s)subset(i1,sarr[i])subset(i1,s)s==0i==0arr[i]>sarr[i]<siarr[i]<si
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

动态规划写法
  • 需要使用二维数组来保存中间所有的动态过程
  • 通常使用二维数组 ,都会把二维数组的下标分别加 1
  • 返回 二维数组的最后一个数组
import numpy as np
def dp_subset(arr,S):
    subset = np.zeros((len(arr),S+1),dtype = bool)
    subset[:,0] = True
    subset[0,:] = False
    subset[0,arr[0]] = True
    for i in range(1,len(arr)):
        for s in range(1,S+1):
            if arr[i]>s:
                subset[i,s] = subset[i-1,s]
            else:
                a = subset[i-1,s-arr[i]] #选择arr[i]
                b = subset[i-1,s] # 不选arr[i]
                subset[i,s] = a or b
    r ,c = subset.shape
    return subset[r-1,c-1]
递归写法
  • 需要把 arr 传递进去
arr = [3,34,4,12,5,2]
# 递归写法
def rec_subset(arr,i,s):
    # 找出口
    if s ==0:
        return True
    if i ==0:
        return arr[0]==s
    if arr[i]>s:
        return rec_subset(arr,i-1,s)
    else:
        a = rec_subset(arr,i-1,s-arr[i]) #选择arr[i]
        b = rec_subset(arr,i-1,s) # 不选arr[i]
        return a or b

参考视频

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值