Bootstrap

区间DP学习笔记:最长回文子序列,多边形三角剖分的最低得分

本期参考:区间 DP:最长回文子序列【基础算法精讲 22】_哔哩哔哩_bilibili

ps:笔记中的代码按本人理解整理,重思路,对比原视频中的代码稍有改动

【如果笔记对你有帮助,欢迎关注&点赞&收藏,收到正反馈会加快更新!谢谢支持!】

题目1:最长回文子序列

516. 最长回文子序列 - 力扣(LeetCode)

  • 思路:用首尾来表示一个序列区间,处理“字符串A =  + 字符串B + "
  • 拆解:
    • 回文串的两种情况:
      1. 一个字符
      2. 回文串A = x + 回文串B + x (其中x为任意字符),比如'aba' 其中x='a'
    • 题目要求最长子回文序列长度(字符串可以任意删字符来变成回文串,只要留下的回文串顺序不变)Eg: 'aba', 'abca', 'abac','abcca'   这四个str留下的回文串为'aba',所以最长子回文序列都等于3。
      因为 字符串A包含回文串A, 字符串B包含回文串B(比如'abac'包含'aba'),所以下面用字符串讨论。
    • 两种处理情况(用“result_A” 表示 字符串A的最长回文子序列)
      1. 字符串A = x + 字符串B + 【result_A = result_B + 2】
      2. 字符串A = x + 字符串B + ≠ y) 【result_A = max{result_xB, result_By}】
        (取 “x + 字符串B” 和 “字符串B + y” 的最大值作为 字符串A的最长回文子序列)
    • 需要从小区间处理到大区间,因为 len(字符串A) ≧ len(字符串B),要先拿到字符串B对应的值,再处理字符串A
  • 代码:
    class Solution:
        def longestPalindromeSubseq(self, s: str) -> int:
            n = len(s)
            dp = [[0]*n for _ in range(n)] #需要二维dp(n*n)分别表示首尾
            
            # i表示首, j表示尾,dp[i][j]表示i到j这个区间的最长子回文序列长度
            # 先确认区间,再处理区间
            for j in range(n):
                dp[j][j] = 1   # 一个字符串的情况,也是长度为1的回文串
                for i in range(j-1, -1, -1):  # i要倒序是因为dp[i]要从dp[i+1]转移过来
                    if s[i] == s[j]: 
                        # 字符串A(i为首,j为尾) = x + 字符串B(i+1为首,j-1为尾) + x     
                        dp[i][j] = dp[i+1][j-1] + 2 
                    else:
                        # 字符串A = x + 字符串B + y (x ≠ y)
                        dp[i][j] = max(dp[i+1][j], dp[i][j-1])
            return dp[0][n-1] # 返回一整个字符串对应的值

题目2: 多边形三角剖分的最低得分

1039. 多边形三角剖分的最低得分 - 力扣(LeetCode)

  • 思路:用首尾来表示一个序列区间,处理“多边形A = 多边形B + 尾组成的边"
  • 拆解
    •         
    • 小区间(三角形)处理到大区间(多边形),因为要调用的值得是已经计算好的,上图例子要计算多边形{BCDEF},得先知道多边形{BCDE},再先要知道{BCD}
  • 代码
    class Solution:
        def minScoreTriangulation(self, values: List[int]) -> int:
            n = len(values)  # n边形
            dp = [[0]*n for _ in range(n)]  # 同上题
            
            # 先确认区间[i,j],i表示首, j表示尾
            for j in range(2, n):  # 从三角形(第三个顶点,索引为2)开始
                for i in range(j-2, -1, -1):  # 要确保有个三角形(所以j-2),遍历到第一个顶点(索引为0)        
                    # 确定多边形之后,以一条边为基准,看如何分割得到的总和最小
                    result = inf  
                    for k in range(i+1, j):  # 以ij这条边为基准,三角形的另一个顶点从i+1遍历到j-1
                        result = min(result, values[i]*values[j]*values[k] + dp[i][k] + dp[k][j])
                    dp[i][j] = result
            
            return dp[0][n-1]
    
    

往期笔记:

状态机DP学习笔记-CSDN博客

树形DP学习笔记(一):树的路径问题-CSDN博客

树形DP学习笔记(二):打家劫舍III & 监控二叉树-CSDN博客

;