力扣hot100—系列2-多维动态规划

零基础理解动态规划:

动态规划就像是"填表格"。我们把一个大问题拆成很多个小问题,把小问题的答案记在表格里。当我们要算大问题时,直接查表利用之前算好的结果,这样就不用重复计算了。


1. 不同路径 (Unique Paths)

题目描述: 一个机器人位于 m×nm \times nm×n 网格的左上角。它每次只能向下或向右移动一步。问到达右下角有多少条不同的路径?

通俗思路:

  • 拆解: 机器人想去 (i, j) 这个格点,它只有两种可能:从上面(i-1, j),或者从左边(i, j-1)
  • 推导: 到达 (i, j) 的总路径数 = 到达上面的路径数 + 到达左边的路径数。
  • 状态转移方程: dp[i][j] = dp[i-1][j] + dp[i][j-1]

答案代码(Python):

python 复制代码
def uniquePaths(m, n):
    # 初始化表格,全部填1(因为第一行和第一列只有一种走法:一直向右或一直向下)
    dp = [[1] * n for _ in range(m)]
    
    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]

2. 最小路径和 (Minimum Path Sum)

题目描述: 给定一个包含非负整数的 m×nm \times nm×n 网格,找出一条从左上角到右下角的路径,使得路径上的数字总和最小。

通俗思路:

  • 拆解: 想让到达 (i, j) 的路径和最小,你要看从上面下来更便宜,还是从左边过来更便宜。
  • 推导: 当前格子的最小和 = 当前格子的数值 + min(上边格子的最小和, 左边格子的最小和)。
  • 状态转移方程: dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])

答案代码:

python 复制代码
def minPathSum(grid):
    m, n = len(grid), len(grid[0])
    dp = [[0] * n for _ in range(m)]
    
    for i in range(m):
        for j in range(n):
            if i == 0 and j == 0: dp[i][j] = grid[0][0] # 起点
            elif i == 0: dp[i][j] = dp[i][j-1] + grid[i][j] # 第一行只能从左来
            elif j == 0: dp[i][j] = dp[i-1][j] + grid[i][j] # 第一列只能从上下来
            else:
                dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
                
    return dp[m-1][n-1]

3. 最长回文子串 (Longest Palindromic Substring)

题目描述: 给你一个字符串 s,找到 s 中最长的回文子串(正着读反着读都一样)。

通俗思路:

  • 拆解: 如果一个字符串 s[i...j] 是回文,且左右两边的字符相等 s[i] == s[j],那么去掉头尾的中间部分 s[i+1...j-1] 也必须是回文。
  • 状态: dp[i][j] 表示从第 i 到第 j 个字符构成的子串是否为回文(True/False)。
  • 推导: dp[i][j] = (s[i] == s[j]) and dp[i+1][j-1]

答案代码:

python 复制代码
def longestPalindrome(s):
    n = len(s)
    if n < 2: return s
    dp = [[False] * n for _ in range(n)]
    # 每个单独的字符都是回文
    for i in range(n): dp[i][i] = True
    
    max_len = 1
    start = 0
    # L 是子串长度
    for L in range(2, n + 1):
        for i in range(n):
            j = L + i - 1 # 右边界
            if j >= n: break
            
            if s[i] == s[j]:
                if j - i < 3: # 长度为2或3时,只要s[i]==s[j]就是回文
                    dp[i][j] = True
                else:
                    dp[i][j] = dp[i+1][j-1]
            
            if dp[i][j] and L > max_len:
                max_len = L
                start = i
    return s[start:start + max_len]

4. 最长公共子序列 (Longest Common Subsequence, LCS)

题目描述: 给定两个字符串 text1text2,返回这两个字符串的最长公共子序列的长度。

通俗思路:

  • 拆解: 比较两个字符串的末尾字符。
  • 情况1: 末尾字符相同。那这个字符一定在公共子序列里。长度 = 1 + 前面部分的LCS
  • 情况2: 末尾字符不同。那最长公共序列要么在 text1 去掉最后一个字符后,要么在 text2 去掉最后一个字符后。
  • 状态转移:
    • 如果 s1[i] == s2[j]: dp[i][j] = dp[i-1][j-1] + 1
    • 否则: dp[i][j] = max(dp[i-1][j], dp[i][j-1])

答案代码:

python 复制代码
def longestCommonSubsequence(text1, text2):
    m, n = len(text1), len(text2)
    # 多申请一行一列处理边界(空字符串情况)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if text1[i-1] == text2[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
                
    return dp[m][n]

5. 编辑距离 (Edit Distance)

题目描述: 给你两个单词 word1word2,请你计算出将 word1 转换成 word2 所使用的最少操作数(插入、删除、替换)。

通俗思路:

想象你在把 word1 变成 word2

  • 如果末尾字符一样:不需要操作。dp[i][j] = dp[i-1][j-1]
  • 如果不一样,你有三种选择:
    1. 插入 一个字符:dp[i][j-1] + 1
    2. 删除 一个字符:dp[i-1][j] + 1
    3. 替换 一个字符:dp[i-1][j-1] + 1
  • 我们要选这三个操作里步数最少的。

答案代码:

python 复制代码
def minDistance(word1, word2):
    m, n = len(word1), len(word2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    # 边界情况:word1 变为空字符串,或空字符串变为 word2
    for i in range(m + 1): dp[i][0] = i
    for j in range(n + 1): dp[0][j] = j
    
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if word1[i-1] == word2[j-1]:
                dp[i][j] = dp[i-1][j-1] # 字符相等,不增加操作
            else:
                # 取 替换、删除、插入 三者中的最小值 + 1
                dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
                
    return dp[m][n]

总结:做多维 DP 的三步走

  1. 定义表格含义: dp[i][j] 代表什么?(例如:到达 (i,j) 的方法数、最长长度、最小代价)。
  2. 找递推关系: 到达当前这一步,前一步是从哪里来的?(上边?左边?还是斜角?)。
  3. 定初始值: 表格的第一行和第一列(最简单的情况)该填什么。
相关推荐
xsyaaaan3 小时前
代码随想录Day31动态规划:1049最后一块石头的重量II_494目标和_474一和零
算法·动态规划
Jay Kay4 小时前
GVPO:Group Variance Policy Optimization
人工智能·算法·机器学习
Epiphany.5564 小时前
蓝桥杯备赛题目-----爆破
算法·职场和发展·蓝桥杯
YuTaoShao4 小时前
【LeetCode 每日一题】1653. 使字符串平衡的最少删除次数——(解法三)DP 空间优化
算法·leetcode·职场和发展
茉莉玫瑰花茶4 小时前
C++ 17 详细特性解析(5)
开发语言·c++·算法
cpp_25014 小时前
P10570 [JRKSJ R8] 网球
数据结构·c++·算法·题解
cpp_25015 小时前
P8377 [PFOI Round1] 暴龙的火锅
数据结构·c++·算法·题解·洛谷
uesowys5 小时前
Apache Spark算法开发指导-Factorization machines classifier
人工智能·算法
TracyCoder1235 小时前
LeetCode Hot100(26/100)——24. 两两交换链表中的节点
leetcode·链表