动态规划(上)

动态规划
  • 1.最优子结构:问题的最优解由相关子问题的最优解组合而成。
  • 2.边界:问题的边界,得到有限的结果
  • 3.动态转移方程:问题每一阶段和下一阶段的关系。

动态规划需要满足两个条件:

  • 问题的状态满足最优性原理。即最优子结构
  • 问题的状态必须满足无后效性。即以前出现状况 和 以前状态的变化过程 不会影响 将来的变化。
动态规划题目特点:
  • 1.计数
    • 有多少种方式走到右下角。
    • 有多少种方法选出 k 个数使得和是 Sum。
  • 2.求最大值最小值
    • 从左上角走到右下角路径的最大数字和
    • 最长上升子序列长度
  • 3.求存在性
    • 取石子游戏,先手是否必胜
    • 能不能选出 k 个数使得和是 Sum。
动态规划组成部分:
  • 1.确定状态:研究最优策略的最后一步;化成子问题。
  • 2.转移方程:根据子问题定义直接得到。
  • 3.初始条件和边界情况:边界考虑周全。
  • 4.计算顺序:利用之前的计算结果。
常见题型:
1. 求最大最小值动态规划
  • 题目描述:有三种硬币,分别面值2元,5元和7元,每种硬币都足够多,买一本书需要27元。如何用 最少 的硬币组合正好付清,不需要对方找钱? 如果拼不出来则返回-1。
  • 解析 d p [ i ] dp[i] dp[i] 表示 i i i 元的书需要的最少硬币数,所以 d p [ i ] = m i n d p [ i − 2 ] + 1 , d p [ i − 5 ] + 1 , d p [ i − 7 ] + 1 dp[i]=min{dp[i-2]+1, dp[i-5]+1, dp[i-7]+1} dp[i]=mindp[i2]+1,dp[i5]+1,dp[i7]+1 。初始条件 d p [ 0 ] = 0 dp[0]=0 dp[0]=0 ,即边界。
  • 注意:注意 d p dp dp 初始化用无穷大 f l o a t ( ′ i n f ′ ) float('inf') floatinf,即如果拼不出则 d p dp dp 值为无穷大。

(1)递归解法:

def f(N):
    if N == 0:
        return 0
    res = float('inf')
    if N >= 2:
        res = min(f(N-2)+1, res)
    if N >= 5:
        res = min(f(N-5)+1, res)
    if N >= 7:
        res = min(f(N-7)+1, res)
    return res
# print(f(27))

(2) dp解法:

def coinChange(A, N):
    dp = [0]*(N+1)
    for i in range(1, N+1):
        dp[i] = float('inf')
        for j in range(len(A)):
            if i >= A[j] and dp[i-A[j]] != float('inf'):
                dp[i] = min(dp[i-A[j]]+1, dp[i])
    if dp[N] == float('inf'):
        return -1
    return dp[N]
# print(coinChange([2,5,7], 27))
2. 计数型动态规划
  • 题目描述:给定 m m m n n n 列的网格,有一个机器人从左上角 ( 0 , 0 ) (0,0) (0,0) 出发,每一步可以向下或者向右走一步,问有多少种方式走到右下角?
  • 解析 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示机器人有多少种方式从左上角走到 ( i , j ) (i,j) (i,j) d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j]=dp[i-1][j]+dp[i][j-1] dp[i][j]=dp[i1][j]+dp[i][j1] 。边界:当 i i i j j j 等0时, d p dp dp 等于1。
  • 注意:注意m和n是行数和列数,返回时需减1。
def uniquePaths(m, n):
    dp = [[0 for i in range(n)]for j in range(m)]
    for i in range(m):
        for j in range(n):
            if i == 0 or j == 0:
                dp[i][j] = 1
            else:
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
    return dp[m-1][n-1]
# print(uniquePaths(4, 3))
3. 存在型动态规划
  • 题目描述:有 n n n 块石头分别在x轴的 0 , 1 , . . . , n − 1 0,1,...,n-1 0,1...,n1 位置,一只青蛙在石头0,想跳到石头 n − 1 n-1 n1,如果青蛙在第 i i i 块石头上,它最多可以向右跳的距离 a i a_i ai,问青蛙能否跳到石头 n − 1 n-1 n1
  • 示例:
输入:a = [2,3,1,1,4]     输出:True
输入:a = [3,2,1,0,4]     输出:False
  • 解析:令 d p [ i ] dp[i] dp[i] 表示青蛙能否跳到石头 i i i 上,状态转移方程为 f [ i ] = O R 0 &lt; = j &lt; i ( d p [ j ] a n d ( j + a [ i ] &gt; = i ) ) f[i] = OR_{0&lt;=j&lt;i}(dp[j] and (j + a[i]&gt;=i)) f[i]=OR0<=j<i(dp[j]and(j+a[i]>=i)) , 初始条件 d p ( 0 ) = T r u e dp(0)=True dp(0)=True
def canJump(A):
    n = len(A)
    dp = [False]*n
    dp[0] = True
    for i in range(1, n):
        for j in range(i):
            if dp[j] and (j + A[j]) >= i:  # 注意索引i与j不要搞错
                dp[i] = True
                break
    return dp[n-1]
print(canJump([2,3,1,1,4]))
print(canJump([3,2,1,0,4]))
  • :此题 使用贪心算法(Greedy)更优,时间复杂度为O(n)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值