Loading

动态规划

通过leetcode上面的5道基础动态规划题目,讲解求解动态规划问题的思路。

定义

对于动态规划问题,通常需要做3件事情:

  1. 问题目标
  2. 状态的定义:\(opt[n]\)
  3. 状态转移方程:\(opt[n] = best\_of(opt[n-1], opt[n-2], ...)\)

最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

子问题:

\(M(j)\): 以j结尾的最大子序和。

\(A[j]\): 数组的第\(j\)个元素。

\[M(j) = \max \{ M(j-1) + A[j], A[j]\} \]

代码:

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums) == 1:  # 数组只有一个元素
            return nums[0]
        max_ret = nums[0]  # 存储最大子串和
        cur_max = last_max = nums[0]  
        for i in range(1, len(nums)):
            cur_max = max(last_max + nums[i], nums[i])
            if cur_max > max_ret:
                max_ret = cur_max
            last_max = cur_max
        return max_ret

代码执行结果:

时间复杂度: \(O(n)\)

最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 \(O(n^2)\)

子问题:

\(L(j)\): 以j结尾的最长上升子序列。

\(A[j]\): 数组的第\(j\)个元素。

\[L(j) = \mathop{\rm{max:}}_{ i \lt j \atop A[i] \lt A[j]} \{L(i)\} + 1 \]

代码:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums_len = len(nums)
        if nums_len <= 1:
            return nums_len
        mem = [1 for _ in range(nums_len)]  # 初始化,存储每个元素结果对应的LIS的长度
        for i in range(1, nums_len):
            for j in range(0, i):
                if nums[j] < nums[i]:
                    mem[i] = max(mem[i], mem[j]+1)
        return max(mem) 

代码执行结果:

时间复杂度: \(O(n^2)\)

零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

示例1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1

示例2:

输入: coins = [2], amount = 3
输出: -1

说明:
你可以认为每种硬币的数量是无限的。

子问题:

\(M(j)\): 总金额j所需要的最少硬币个数。

\[M(j) = \min_i \{M(j-v_i)\} + 1 \]

代码:

class Solution(object):
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        if amount == 0:
            return 0
        if len(coins) == 0:
            return -1
        if len(coins) == 1 and coins[0] > amount:
            return -1
        mem = [-1 for i in range(amount + 1)]  # 建立存储空间并初始化
        mem[0] = 0
        for i in range(1, amount + 1):
            cur_min = amount + 1
            for c in coins:
                if c <= i:  # 当钱币面值小于当前需要凑的金额时
                    cur_min = min(mem[i-c], cur_min)
            mem[i] = cur_min + 1 if cur_min < amount+1 else amount+1
        if mem[-1] == amount + 1:
            return -1
        else:
            return mem[-1]

代码执行结果:

时间复杂度: \(O(n)\)

0-1背包问题

找不到这个问题,暂时跳过。

编辑距离

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符

示例1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

子问题:

\(T(i,j)\): 把\(A[1 \cdots i]\)转换成\(B[1 \cdots j]\)的最小代价。

\[M(j) = \min_i \{M(j-v_i)\} + 1 \]

参考一下那本书,里面的编辑距离公式,有点复杂,暂时跳过。

代码:

class Solution(object):
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        if amount == 0:
            return 0
        if len(coins) == 0:
            return -1
        if len(coins) == 1 and coins[0] > amount:
            return -1
        mem = [-1 for i in range(amount + 1)]  # 建立存储空间并初始化
        mem[0] = 0
        for i in range(1, amount + 1):
            cur_min = amount + 1
            for c in coins:
                if c <= i:  # 当钱币面值小于当前需要凑的金额时
                    cur_min = min(mem[i-c], cur_min)
            mem[i] = cur_min + 1 if cur_min < amount+1 else amount+1
        if mem[-1] == amount + 1:
            return -1
        else:
            return mem[-1]

代码执行结果:

posted @ 2021-12-29 11:56  活用数据  阅读(33)  评论(0编辑  收藏  举报