当前位置: 首页 > news >正文

LeetCode Hot100 刷题笔记(8)—— 动态规划(一维、二维)

目录

前言

一、动态规划

1. 爬楼梯

2. 杨辉三角

3. 打家劫舍

4. 完全平方数

5. 零钱兑换

6. 单词拆分

7. 最长递增子序列

8. 乘积最大子数组

9. 分割等和子集

10. 最长有效括号

二、多维动态规划

11. 不同路径

12. 最小路径和

13. 最长回文子串

14. 最长公共子序列

15. 编辑距离


前言

一、动态规划:爬楼梯,杨辉三角,打家劫舍,完全平方数,零钱兑换,单词拆分,最长递增子序列,乘积最大子数组,分割等和子集,最长有效括号。

二、多维动态规划:不同路径,最小路径和,最长回文子串,最长公共子序列,编辑距离。

*** Trick

动态规划步骤:1. 确定初始状态;2. 确定边界条件;3. 确定状态转移方程。

*** 常考:0-1背包问题(对应题目:9、14、15)

class Solution(object):
    def canPartition(self, nums):
        # 0-1背包问题
        sums = sum(nums)
        if sums % 2 != 0:
            return False
        target = sums // 2
        n = len(nums)
        dp = [[False] * (target+1) for _ in range(n)]
        dp[0][0] = True
        for i in range(1, n):
            dp[i][0] = True
        for j in range(1, target+1):
            dp[0][j] = False
        for i in range(1, n):
            for j in range(1, target+1):
                if j >= nums[i]:
                    dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]]
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[n-1][target]

一、动态规划

1. 爬楼梯

原题链接:70. 爬楼梯 - 力扣(LeetCode)

# 1. 初始状态:爬到第1阶、第2届台阶的方法分别有1、2种
# 2. 确定边界条件
# 3. 动态转移方程:s(n) = s(n-1) + s(n-2)
class Solution(object):
    def climbStairs(self, n):
        s = [1, 2]   
        if n <=2:
            return s[n-1]
        else:
            for i in range(2, n):
                s.append(s[-1]+s[-2])
            return s[-1]

2. 杨辉三角

原题链接:118. 杨辉三角 - 力扣(LeetCode)

# 1. 初始状态:dp = [[1] * i for i in range(1, numRows+1)]
# 2. 边界条件:2<=i<numRows, 1<=j<numRows-2
# 3. 动态转移方程:dp[i][j] = dp[i][j-1] + dp[i][j]
class Solution(object):
    def generate(self, numRows):
        dp = [[1]*i for i in range(1, numRows+1)]
        if i<2:
            return dp
        for i in range(2, numRows):
            for j in range(1, i):
                dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
        return dp

3. 打家劫舍

原题链接:198. 打家劫舍 - 力扣(LeetCode)

class Solution(object):
    def rob(self, nums):
        nums.insert(0, 0)
        nums.insert(1, 0)
        dp = [0 for i in range(len(nums))]

        for i in range(2, len(nums)):
            dp[i] = max(dp[i-1], dp[i-2]+nums[i])
        return dp[-1]

4. 完全平方数

原题链接:279. 完全平方数 - 力扣(LeetCode)

# 1. 初始状态:dp = [i for i in range(n+1)]
# 2. 边界条件: wihle i-j*j >=0
# 3. 动态转移方程:dp[i] = min(dp[i-j*j]+1, dp[i])
class Solution(object):
    def numSquares(self, n):
        dp = [i for i in range(n+1)]
        for i in range(1, n+1):
            j = 1
            while i-j*j >= 0:
                dp[i] = min(dp[i-j*j]+1, dp[i])
                j+=1
        return dp[-1]

5. 零钱兑换

原题链接:322. 零钱兑换 - 力扣(LeetCode)

# 1. 初始状态:dp = [float("inf")] * (amount+1)
# 2. 边界条件:i-coin[j] >=0
# 3. 状态方程:dp[i] = min(dp[i-coin[j]] + 1, dp[i]), for j in len(coins)
class Solution(object):
    def coinChange(self, coins, amount):
        dp = [float("inf")] * (amount+1)
        dp[0] = 0
        for i in range(1, amount+1):
            for j in range(len(coins)):
                if i-coins[j] >= 0:
                    dp[i] = min(dp[i-coins[j]] + 1, dp[i])
        return dp[-1] if dp[-1]!=float("inf") else -1

6. 单词拆分

原题链接:139. 单词拆分 - 力扣(LeetCode)

class Solution(object):
    def wordBreak(self, s, wordDict):
        dp = [0] + [False for i in range(1, len(s)+1)]
        for i in range(len(s)+1):
            if dp[i] == True:
                for j in range(i+1, len(s)+1):
                    if s[i:j] in wordDict:
                        dp[j] =  True
        return dp[-1]

7. 最长递增子序列

原题链接:300. 最长递增子序列 - 力扣(LeetCode)

class Solution(object):
    def lengthOfLIS(self, nums):
        dp = [1 for i in range(len(nums))]
        for i in range(len(nums)):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[j]+1, dp[i])
        return max(dp)

8. 乘积最大子数组

原题链接:152. 乘积最大子数组 - 力扣(LeetCode)

class Solution(object):
    def maxProduct(self, nums):
        # 初始状态:max_pre = min_pre = num[0]
        # 状态转移方程:max_pre = max(max_pre*nums[i]), min_pre*nums[i], nums[i])
        res = nums[0]
        max_pre = min_pre = nums[0]
        for i in range(1, len(nums)):
            max_pre_ = max_pre * nums[i]
            min_pre_ = min_pre * nums[i]

            max_pre = max(max_pre_, min_pre_, nums[i])
            min_pre = min(max_pre_, min_pre_, nums[i])

            res = max(res, max_pre)
        return res

9. 分割等和子集

原题链接:416. 分割等和子集 - 力扣(LeetCode)

class Solution(object):
    def canPartition(self, nums):
        # 0-1背包问题
        sums = sum(nums)
        if sums % 2 != 0:
            return False
        target = sums // 2
        n = len(nums)
        dp = [[False] * (target+1) for _ in range(n)]
        dp[0][0] = True
        for i in range(1, n):
            dp[i][0] = True
        for j in range(1, target+1):
            dp[0][j] = False
        for i in range(1, n):
            for j in range(1, target+1):
                if j >= nums[i]:
                    dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]]
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[n-1][target]

10. 最长有效括号

原题链接:32. 最长有效括号 - 力扣(LeetCode)

class Solution(object):
    def longestValidParentheses(self, s):
        stack = [-1]
        res = 0
        for i in range(len(s)):
            if s[i] == '(':
                stack.append(i)
            else:
                stack.pop()
                if not stack:
                    stack.append(i)
                else:
                    res = max(res, i-stack[-1])
        return res

二、多维动态规划

11. 不同路径

原题链接:62. 不同路径 - 力扣(LeetCode)

class Solution(object):
    def uniquePaths(self, m, n):
        # 初始状态dp[i][0] = 1, dp[0][j] = 1
        # 状态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]
        dp = [[0] * n for _ in range(m)]
        for i in range(m):
            dp[i][0] = 1
        for j in range(n):
            dp[0][j] = 1
        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[-1][-1]

12. 最小路径和

原题链接:64. 最小路径和 - 力扣(LeetCode)

class Solution(object):
    def minPathSum(self, grid):
        # dp[i][0] = dp[i-1] + grid[i][0], dp[0][j] = dp[0][j-1] + grid[0][j]
        # dp[i][j] = min(dp[i-1][j] + grid[i][j], dp[i][j-1]+ grid[i][j])

        m, n =  len(grid), len(grid[0])
        dp = [[0] * n for _ in range(m)]
        dp[0][0] = grid[0][0]
        for i in range(1, m):
            dp[i][0] = dp[i-1][0] + grid[i][0]
        for j in range(1, n):
            dp[0][j] = dp[0][j-1] + grid[0][j]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = min(dp[i-1][j] + grid[i][j], dp[i][j-1]+ grid[i][j])
        return dp[-1][-1]

13. 最长回文子串

原题链接:5. 最长回文子串 - 力扣(LeetCode)

class Solution(object):
    def longestPalindrome(self, s):
        n = len(s)
        dp = [[False] * n for _ in range(n)]
        start, max_len = 0, 0
        for right in range(n):
            for left in range(right+1):
                span = right - left + 1
                if span == 1:
                    dp[left][right] = True
                elif span == 2:
                    dp[left][right] = s[left] == s[right]
                else:
                    dp[left][right] = dp[left+1][right-1] and s[left] == s[right]
                
                if dp[left][right]:
                    if span > max_len:
                        max_len = span
                        start = left
        return s[start: start+max_len]

14. 最长公共子序列

原题链接:1143. 最长公共子序列 - 力扣(LeetCode)

class Solution(object):
    def longestCommonSubsequence(self, text1, text2):
        # 0-1 背包问题
        # if t1 == t2: dp[i+1][j+1] = dp[i][j] + 1
        # else: dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1])
        m, n = len(text1), len(text2)
        dp = [[0]* (n+1) for _ in range(m+1)]
        for i, t1 in enumerate(text1):
            for j, t2 in enumerate(text2):
                if t1 == t2:
                    dp[i+1][j+1] = dp[i][j] + 1
                else:
                    dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1])
        return dp[-1][-1]

15. 编辑距离

原题链接:72. 编辑距离 - 力扣(LeetCode)

class Solution(object):
    def minDistance(self, word1, word2):
        # 0-1 背包问题
        # 动态转移方程:dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1分别对应插入、删除、替换操作
        """
            0 r o s
        0   0 1 2 3
        h   1
        o   2
        r   3
        s   4
        e   5
        """
        m, n = len(word1), len(word2)
        dp = [[0] * (n+1) for _ in range(m+1)]
        for i in range(1, m+1):
            dp[i][0] = i
        for j in range(1, n+1):
            dp[0][j] = j
        for i, w1 in enumerate(word1):
            for j, w2 in enumerate(word2):
                if w1 == w2:
                    dp[i+1][j+1] = dp[i][j]
                else:
                    dp[i+1][j+1] = min(dp[i+1][j], dp[i][j+1], dp[i][j]) + 1 
        return dp[-1][-1]

相关文章:

  • Python下的图像库:pillow的基本使用方法介绍
  • R语言基础包可视化(一:axis函数)
  • JDK 1.7 HashMap 死循环问题
  • LS-LINUX-002 简易创建SSH
  • 如何实现外观模式?
  • 22 | 如何继续提升 Go 开发技术?
  • java设计模式-外观模式
  • 【人工智能】深入解析自注意力机制:AI大语言模型的核心引擎
  • 球类(继承和多态)
  • configMAX_SYSCALL_INTERRUPT_PRIORITY和configKERNEL_INTERRUPT_PRIORITY
  • 力扣刷题DAY10(动态规划-线性DP)
  • rcore day6
  • [ctfshow web入门] web23
  • cdw2: TypeScript
  • 牛客网:树的高度 ← 根节点为 0 号节点
  • 脚本启动 Java 程序
  • 工程师 - FTDI SPI converter
  • async/await 异步编程
  • 将飞帆制作的网页作为 div 集成到自己的网页中
  • C语言之九九乘法表