动态规划(一)

动态规划

动态规划题目特点

  1. 计数
    • 有多少种方式走到右下角
    • 有多少种方法选出k个数使得和是sum
  2. 求最大值最小值
    • 从左上角走到右下角路径的最大数字和
    • 最长上升子序列长度
  3. 求存在性
    • 取石子游戏,先手是否必胜
    • 能不能选出k个数使得和是sum

动态规划组成部分:

  1. 确定状态 f[x]:解动态规划的时候需要开一个数组,数组的每个f[i]或f[i][j]代表什么

    • 最后一步
    • 子问题
  2. 转移方程:根据子问题写出转移方程

  3. 初始条件和边界情况

    • 初始条件:用转移方程算不出来的,需要手工定义
    • 边界情况:数组不能越界
  4. 计算顺序的确定

    • 从小到大
    • 从大到小

求最值型动态规划

leetcode322:给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。你可以认为每种硬币的数量是无限的。

问题分析:

​ 假设 coins = [2, 3, 5], amount = 27

  1. 确定状态 f[x] = 最少用多少枚硬币拼出x

    • 考虑子问题和最后一步

    • 假设最优策略为k枚硬币a_1,a_2,…,a_k,所以一定有一枚最后的硬币a_k,前面硬币的总面值为27-a_k

    • 因为是最优策略所以拼出27-a_k的硬币数一定最少

    • 原问题:最少用多少枚硬币拼出a_k

    • 子问题:最少用多少枚硬币拼出27-a_k

    • 对于任意的x f[x] = min{f[x-2]+1, f[x-5]+1, f[x-7]+1}

  2. 转移方程

    • f[x] = min{f[x-2]+1, f[x-5]+1, f[x-7]+1}
  3. 初始条件和边界情况

    • 初始条件:f[0] = 0

    • 边界情况:如果不能拼出Y, 定义f[Y] = 正无穷

  4. 计算顺序的确定

    • 初始条件:f[0] = 0

    • 计算:f[1], f[2],…, f[27]

    • 当计算f[x]时,f[x-2],f[x-5],f[x-7]都已经有结果了

class Solution(object):
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        f = []
        n = len(coins)  # number of kinds of coins

        # 初始化
        f = [0 for _ in range(amount + 1)]

        # 计算f[1],f[2],...,f[amount]
        for i in range(1, amount+1):
            f[i] = float('inf') 
            # 最后一枚硬币
            for c in coins:
                if i - c >= 0:
                    f[i] = min(f[i-c]+1, f[i])
        return -1 if math.isinf(f[amount]) else f[-1]

求计数型动态规划

LC62不同路径:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?

问题分析:

  1. 确定状态 f[i][j]为机器人有多少种方式走到[i][j]

    • 考虑子问题和最后一步
      • 最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步(向右或者向下)
      • 设右下角坐标为(m-1,n-1),那么前一步机器人一定在(m-2,n-1)或者(m-1,n-2)
      • 假设机器人有X种方式从左上角走到(m-2,n-1),有Y种方式走到(m-1,n-2),则机器人有X+Y种方式走到(m-1,n-1)
      • 问题转化为,机器人有 多少种方式从左上角走到(m-2,n-1)和(m-1,n-2)
  2. 转移方程 : f[i][j] = f[i-1][j] + f[i][j-1]

  3. 初始条件和边界情况

    • 初始条件:f[0][0]=1

    • 边界情况:i=0或j=0,f[i][j] = 1

  4. 计算顺序的确定

    • 初始条件:f[0][0]=1
    • 计算第0行:f[0][0],f[0][1],…,f[0][n-1]
    • 计算第1行:f[1][0],f[1][1],…,f[1][n-1]
    • ​ …
    • 答案是 f[m-1][n-1]
class Solution(object):
    def uniquePaths(self, m, n):
        """
        :type m: int
        :type n: int
        :rtype: int
        """
        # 初始化
        dp = [[1 for _ in range(n)] for _ in range(m)]
        # 计算每一个dp[i][j]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        return dp[m - 1][n - 1]

求存在型动态规划

55. 跳跃游戏:给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。

问题分析:

  1. 确定状态 f[i]能否到达下标i

    • 考虑子问题和最后一步
      • 最后一步:如果能到达最后一个下标n-1,考虑最后一步
      • 如果最后一步是从下标i的位置跳过来,需要满足两个条件:
        1. 可以跳跃到下标i的位置
        2. 最后一步不超过跳跃的最大距离: n − 1 − i < = n u m s [ i ] n-1-i<=nums[i] n1i<=nums[i]
      • 原问题:能否到达最后一个下标 n − 1 n-1 n1
      • 子问题:能否到达下标 i   ( i < n − 1 ) i\ (i<n-1) i (i<n1)
  2. 转移方程 : f [ j ] = O R 0 < = i < j ( f [ i ]   a n d   i + n u m s [ i ] > = j ) f[j] = OR_{0<=i<j}(f[i]\ and \ i + nums[i] >= j) f[j]=OR0<=i<j(f[i] and i+nums[i]>=j)

  3. 初始条件和边界情况

    • 初始条件:f[0] = True
  4. 计算顺序的确定

    • 初始条件:f[0] = True
    • 计算:f[1],f[2],…,f[n-1]
  5. 时间复杂度o( N 2 N^2 N2),空间复杂度o( N N N)

class Solution(object):
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        n = len(nums)
        f = [True for _ in range(n)]
        for j in range (1,n):
            f[j] = False
            # previous step
            # last jump from i to j
            for i in range(j):
                if f[i] and i + nums[i] >= j:
                    f[j] = True
                    break
        return f[n-1]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值