动态规划(一)

5. 最长回文子串

class Solution:
    def longestPalindrome(self, s: str) -> str:
        # # 中心扩展
        # # 遍历字符串中心(可能一个字符,可能两个字符),向外扩展,找到每个中心对应的最长回文子串

        # def palindrome(s, i, j):
        #     if j >= len(s):
        #         return s[i]
        #     while (i >= 0 and j < len(s) and s[i] == s[j]):
        #         i -= 1
        #         j += 1
        #     return s[i+1:j]

        # res = ""
        # if len(s) <= 1:
        #     return s
        # for i in range(len(s)):
        #     s1 = palindrome(s, i, i)  # 以第 i 个字符为中心的最长子串
        #     s2 = palindrome(s, i, i+1)  # 以第 i 个和第 i+1 个字符为中心的最长子串
        #     res = res if len(res) >= len(s1) else s1
        #     res = res if len(res) >= len(s2) else s2
        # return res

        # 动态规划
        # 状态转移:dp[i][j] = dp[i+1][j-1] and s[i] == s[j]
        # 转移顺序:从长度较短的字符串向长度较长的字符串转移,注意循环顺序
        # 边界条件:dp[i][i] = True, dp[i][i+1] = s[i] == s[i+1]
        n = len(s)
        if n < 2:
            return s
        # 初始化
        dp = [[False] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = True
        # 枚举字符串长度
        begin = 0
        max_len = 1
        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]:
                    dp[i][j] = False
                else:
                    if j-i < 3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i+1][j-1]
                # 判断最长长度,记录起始位置和长度
                if dp[i][j] and L > max_len:
                    max_len = L
                    begin = i
        return s[begin: begin+max_len]

    

22. 括号生成

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        res = []
        def backtrace(ans, l, r):
            if l == n and r == n:
                res.append(''.join(ans))
            if l < n:
                ans.append('(')
                backtrace(ans, l+1, r)
                ans.pop()
            if r < l:
                ans.append(')')
                backtrace(ans, l, r+1)
                ans.pop()
        backtrace([], 0, 0)
        return res

32. 最长有效括号

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        # # 动态规划
        # # 状态转移:if s[i] == ')' and s[i-1] == '(', dp[i] = dp[i-2]+2
        # # if s[i] == ')' and s[i-1] == ')', dp[i] = dp[i-1] + dp[i-2-dp[i-1]]+2
        # res = 0
        # n = len(s)
        # dp = [0 for _ in range(n)]
        # for i in range(n):
        #     if s[i] == ')':
        #         if i-1 >= 0 and s[i-1] == '(':
        #             dp[i] = dp[i-2]+2
        #             res = max(res, dp[i])
        #             continue
        #         if i-1 >= 0 and s[i-1] == ')' and i-1-dp[i-1] >= 0 and s[i-1-dp[i-1]] == '(':
        #             dp[i] = dp[i-1] + dp[i-2-dp[i-1]]+2
        #             res = max(res, dp[i])
        # return res
        
        # 栈
        # 左括号下标入栈,右括号出栈
        # 栈顶元素为【最后一个没有被匹配的右括号的下标】
        # 如果栈为空,说明当前的右括号没有被匹配,把下标入栈,作为【最后一个没有被匹配的右括号的下标】
        # 如果栈非空,当前右括号的下标减去栈顶元素,即为【以该右括号结尾的最长有效长度】
        res = 0
        stack = [-1]
        if len(s) <= 1:
            return res
        for i in range(len(s)):
            # # 左括号下标入栈
            if s[i] == '(':
                stack.append(i)
                continue
            else:
                # 右括号出栈
                stack.pop()
                # 栈为空,当前右括号没有被匹配,把下标入栈
                if len(stack) == 0:
                    stack.append(i)
                # 栈非空,当前括号被匹配,更新【以该右括号结尾的最长有效长度】
                else:
                    res = max(res, i-stack[-1])
        return res

42. 接雨水

class Solution:
    def trap(self, height: List[int]) -> int:
        # 双指针
        res = 0
        left = 0
        right = len(height) - 1
        l_max = r_max = 0
        while left <= right:
            l_max = max(l_max, height[left])
            r_max = max(r_max, height[right])
            if l_max <= r_max:
                res += max(0, l_max - height[left])
                left += 1
            else:
                res += max(0, r_max - height[right])
                right -= 1
        return res

        # # 备忘录优化
        # res = 0
        # l = [0 for i in range(len(height))]
        # r = [0 for i in range(len(height))]
        # l[0] = height[0]
        # r[-1] = height[-1]
        # for i in range(1, len(height)):
        #     l[i] = max(l[i-1], height[i])
        # for j in range(len(height)-2, -1, -1):
        #     r[j] = max(r[j+1], height[j])
        # for i in range(1, len(height)):
        #     res += max(min(l[i], r[i]) - height[i], 0)  # 和0取max
        # return res

        # # 暴力解法
        # res = 0
        # for i in range(len(height)):
        #     l = r = 0
        #     for j in range(i):
        #         l = max(l, height[j])
        #     for k in range(i+1, len(height)):
        #         r = max(r, height[k])
        #     res += max(min(l, r) - height[i], 0)  # 和0取max
        # return res

45. 跳跃游戏 II

class Solution:
    def jump(self, nums: List[int]) -> int:
        # 正向查找,维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1
        # 时间复杂度O(N),空间复杂度O(1)
        res = 0
        end = 0
        max_pos = 0
        for i in range(len(nums)-1):
            max_pos = max(max_pos, i + nums[i])
            if i == end:
                end = max_pos
                res += 1
        return res


        # # 贪心算法,通过局部最优求解全局最优,反向查找每次最远的可到达位置
        # # 时间复杂度 O(N),空间复杂度 O(1)
        # res = 0
        # pos = len(nums) - 1
        # while pos > 0:
        #     for i in range(len(nums)):
        #         if i + nums[i] >= pos:
        #             pos = i
        #             res += 1
        #             break
        # return res

53. 最大子数组和

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # # 动态规划
        # # dp[i]表示以 nums[i]结尾的最大连续子数组和(nums[i]一定被选取)
        # # 状态转移:dp[i] = max(dp[i-1]+nums[i], nums[i])
        # # 时间复杂度 O(n), 空间复杂度 O(n)
        # n = len(nums)
        # if n == 0:
        #     return 0
        # dp = [0 for _ in range(n)]
        # dp[0] = nums[0]
        # for i in range(1, n):
        #     dp[i] = max(dp[i-1]+nums[i], nums[i])
        # return max(dp)

        # 优化空间复杂度 O(1), dp[i]只依赖 dp[i-1]
        n = len(nums)
        if n == 0:
            return 0
        dp_1 = nums[0]
        res = dp_1
        for i in range(1, n):
            dp_1 = max(dp_1+nums[i], nums[i])
            res = max(res, dp_1)
        return res

55. 跳跃游戏

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        # 贪心,维护最远能到达位置max_pos,其中max_pos只在索引小于max_pos时更新
        l = len(nums)
        if l <= 1:
            return True
        max_pos = nums[0]
        for i in range(1, l):
            if i <= max_pos:
                max_pos = max(max_pos, i + nums[i])
                if max_pos >= l-1:
                    return True
        return False

62. 不同路径

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # 动态规划 二维
        #  dp[i][j]表示到达(i, j)位置的路径总数
        # 状态转移:dp[i][j] = dp[i-1][j] + dp[i][j-1]
        dp = [[0]*n for _ in range(m)]
        # 初始化 (第一行和第一列为 1)
        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[m-1][n-1]

63. 不同路径 II

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        # # 动态规划 二维
        # # dp[i][j] 表示到达(i,j)位置的路径数
        # # 状态转移:if grid[i][j] == 1, dp[i][j] = 0; 否则 dp[i][j] = dp[i-1][j] + dp[i][j-1]
        # m = len(obstacleGrid)
        # n = len(obstacleGrid[0])
        # dp = [[0]*n for _ in range(m)]
        # # 首位单独处理
        # if m == 0 or obstacleGrid[0][0] == 1:
        #     return 0
        # # 初始化
        # dp[0][0] = 1
        # # 迭代
        # for i in range(m):
        #     for j in range(n):
        #         if i == 0 and j == 0:
        #             continue
        #         elif obstacleGrid[i][j] == 1:
        #             dp[i][j] = 0
        #         else:
        #             dp[i][j] = (dp[i-1][j] if i >= 1 else 0) + (dp[i][j-1] if j >= 1 else 0)
        # return dp[m-1][n-1]

        # 空间优化,优化到一维数组,分行计算,每次计算后覆盖旧的
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])
        # 首位单独处理
        if m == 0 or obstacleGrid[0][0] == 1:
            return 0
        dp = [0 for _ in range(n)]
        dp[0] = 1
        for i in range(m):
            for j in range(n):
                # 每行第一个(j=0)延续上一个值(如果没有障碍,就一直是 1,如果有障碍,障碍之后都是 0)
                if obstacleGrid[i][j] == 1:
                    dp[j] = 0
                elif j >= 1:
                    dp[j] = dp[j] + dp[j-1]
        return dp[n-1]

64. 最小路径和

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        # # 动态规划 二维
        # # dp[i][j]表示到达(i,j)位置的最小路径
        # # 状态转移:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
        # m = len(grid)
        # n = 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], dp[i][j-1]) + grid[i][j]
        # return dp[m-1][n-1]

        # 空间优化 一维
        m = len(grid)
        n = len(grid[0])
        dp = [0 for _ in range(n)]
        # 初始化 首行
        dp[0] = grid[0][0]
        for j in range(1, n):
            dp[j] = dp[j-1] + grid[0][j]
        for i in range(1, m):
            for j in range(n):
                dp[j] = min(dp[j], dp[j-1] if j >= 1 else float('inf')) + grid[i][j]
        return dp[n-1]

70. 爬楼梯

class Solution:
    def climbStairs(self, n: int) -> int:
        # # 动态规划 一维数组
        # # dp[i] 表示爬到第 i 阶的方法数
        # # 状态转移:dp[i] = dp[i-1] + dp[i-2]
        # if n <= 1:
        #     return 1
        # dp = [0 for _ in range(n)]
        # dp[0] = 1  # 0 表示一阶台阶
        # dp[1] = 2
        # for i in range(2, n):
        #     dp[i] = dp[i-1] + dp[i-2]
        # return dp[n-1]

        # 空间优化
        if n <= 1:
            return 1
        dp_1 = 2
        dp_2 = 1
        for i in range(2, n):
            dp = dp_1 + dp_2
            dp_2 = dp_1
            dp_1 = dp
        return dp_1

91. 解码方法

class Solution:
    def numDecodings(self, s: str) -> int:
        # 动态规划
        # dp[i] 表示以第 i 个结尾的字符串可解码的总数
        # 状态转移:两种情况,一种是第 i 个单独处理,if s[i] != '0', dp[i] = dp[i-1]
        # 第二种是,第 i 个和第 i-1 个一起处理,if i >=1 and s[i-1] != '0' and int(s[i-1]) * 10 + int(s[i]) <= 26, dp[i] = dp[i-2]
        n = len(s)
        dp = [0 for _ in range(n)]
        for i in range(n):
            if s[i] != '0':
                dp[i] = dp[i-1] if i >= 1 else 1
            if i >=1 and s[i-1] != '0' and int(s[i-1]) * 10 + int(s[i]) <= 26:
                dp[i] += dp[i-2] if i >= 2 else 1
        return dp[n-1]

95. 不同的二叉搜索树 II

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def generateTrees(self, n: int) -> List[TreeNode]:
        # 回溯,外层循环遍历 1~n 所有结点,作为根结点,内层双层递归分别求出左子树和右子树
        def generate(start, end):
            if start > end:
                return [None]
            res = []
            for i in range(start, end+1):
                leftTrees = generate(start, i-1)
                rightTrees = generate(i+1, end)
                for l in leftTrees:
                    for r in rightTrees:
                        curTree = TreeNode(i)
                        curTree.left = l
                        curTree.right = r
                        res.append(curTree)
            return res

        if n < 1:
            return []
        return generate(1, n)

96. 不同的二叉搜索树

class Solution:
    def numTrees(self, n: int) -> int:
        # 动态规划
        # dp[n]表示 1-n 个数能组成多少种不同的二叉搜索树
        # 状态转移
        # F(i, n) 表示以 第 i 个为根节点,1-n 个数能组成的个数
        # dp[n] = F(1, n) + F(2, n) + ... + F(n, n) 初始值 dp[0] = 1, dp[1] = 1
        # F(j, n) = dp[j-1] * dp[n-j]
        dp = [0 for _ in range(n+1)]
        dp[0] = 1
        dp[1] = 1
        for i in range(2, n+1):
            for j in range(1, i+1):
                dp[i] += dp[j-1] * dp[i-j]
        return dp[n]

97. 交错字符串

class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        # dp dp[i][j]表示s1的前i个字符和s2的前j个字符是否能组成s3的前i+j个字符
        m = len(s1)
        n = len(s2)
        if (n + m != len(s3)):
            return False
        dp = [[0]*(n+1) for _ in range(m+1)] # 注意行列
        dp[0][0] = 1
        for i in range(1, m+1):
            if s1[i-1] == s3[i-1]:
                dp[i][0] = max(dp[i][0], dp[i-1][0])
        for j in range(1, n+1):
            if s2[j-1] == s3[j-1]:
                dp[0][j] = max(dp[0][j], dp[0][j-1])
        for i in range(1, m+1):
            for j in range(1, n+1):
                if s1[i-1] == s3[i+j-1]:
                    dp[i][j] = max(dp[i][j], dp[i-1][j])
                if s2[j-1] == s3[i+j-1]:
                    dp[i][j] = max(dp[i][j], dp[i][j-1])
        print(dp)
        return dp[m][n] == 1
        
        # m = len(s1)
        # n = len(s2)
        # if (n + m != len(s3)):
        #     return False
        # dp = [[0]*(n+1) for _ in range(m+1)] # 注意行列
        # dp[0][0] = 1
        # for i in range(m+1):
        #     for j in range(n+1):
        #         if i > 0 and s1[i-1] == s3[i+j-1]:
        #             dp[i][j] = max(dp[i][j], dp[i-1][j])
        #         if j > 0 and s2[j-1] == s3[i+j-1]:
        #             dp[i][j] = max(dp[i][j], dp[i][j-1])
        # return dp[m][n] == 1

115. 不同的子序列

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        # dp dp[i][j]表示s[:j]的子序列中出现t[:i]的个数
        # 初始化,当i=0时,第一行为1;当j=0时,第一列为0
        # 转移方程:如果s[j] == t[i], dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
        # 如果s[j] != t[i], dp[i][j] = dp[i][j-1]
        m = len(t)
        n = len(s)
        dp = [[0]*(n+1) for _ in range(m+1)]
        for i in range(m+1):
            dp[i][0] = 0
        for j in range(n+1):
            dp[0][j] = 1
        for i in range(1, m+1):
            for j in range(1, n+1):
                if s[j-1] == t[i-1]:
                    dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
                else:
                    dp[i][j] = dp[i][j-1]
        return dp[-1][-1]
        # 交替滚动一维 dp
        # 原地滚动一维 dp,借助变量

118. 杨辉三角

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        res = list()
        for i in range(numRows):
            row = list()
            for j in range(i+1):
                if j == 0 or j == i:
                    row.append(1)
                else:
                    row.append(res[i-1][j-1] + res[i-1][j])
            res.append(row)
        return res

120. 三角形最小路径和

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        # # dp dp[i][j]表示到达triangle[i][j]的最短路径和
        # m = len(triangle)
        # n = len(triangle[-1])
        # dp = [[0]*n for _ in range(m)]
        # dp[0][0] = triangle[0][0]
        # for i in range(1, m):
        #     for j in range(i+1):
        #         if j == 0:
        #             dp[i][j] = dp[i-1][j] + triangle[i][j]
        #         elif j == i:
        #             dp[i][j] = dp[i-1][j-1] + triangle[i][j]
        #         else:
        #             dp[i][j] = min(dp[i-1][j], dp[i-1][j-1]) + triangle[i][j]
        # return min(dp[m-1])

        # # 空间优化 滚动数组
        # m = len(triangle)
        # n = len(triangle[-1])
        # pre, cur = [0] * n, [0] * n
        # pre[0] = triangle[0][0]
        # cur[0] = triangle[0][0]  # 处理只有一行情况
        # for i in range(1, m):
        #     for j in range(i+1):
        #         if j == 0:
        #             cur[j] = pre[j] + triangle[i][j]
        #         elif j == i:
        #             cur[j] = pre[j-1] + triangle[i][j]
        #         else:
        #             cur[j] = min(pre[j], pre[j-1]) + triangle[i][j]
        #     pre = cur.copy()
        # return min(cur)

        # 空间优化 一维数组
        m = len(triangle)
        n = len(triangle[-1])
        cur = [0] * n
        cur[0] = triangle[0][0]
        for i in range(1, m):
            for j in range(i, -1, -1):  # 倒序,保证 cur[j-1] 未覆盖
                if j == 0:
                    cur[j] += triangle[i][j]
                elif j == i:
                    cur[j] = cur[j-1] + triangle[i][j]
                else:
                    cur[j] = min(cur[j], cur[j-1]) + triangle[i][j]
        return min(cur)

121. 买卖股票的最佳时机

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # # 一次遍历,记录到当天为止的最小股票价格和最大利润
        # n = len(prices)
        # inf = int(1e9)
        # minprice = inf
        # maxProfit = 0
        # for i in range(n):
        #     minprice = min(minprice, prices[i])
        #     maxProfit = max(maxProfit, prices[i] - minprice)
        # return maxProfit

        # dp dp[i]表示截止到第 i 天能获取的最大利润
        # 判断第i天是否卖出股票
        # dp[i] = max(dp[i-1], prices[i] - minPrice)
        # 边界条件,dp[0] = 0
        n = len(prices)
        minPrice = prices[0]
        dp = [0] * n
        for i in range(1, n):
            minPrice = min(minPrice, prices[i])
            dp[i] = max(dp[i-1], prices[i] - minPrice)
        return max(dp)

122. 买卖股票的最佳时机 II

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # # dp[i][0] 表示第 i 天交易完后手里没有股票的最大利润
        # # dp[i][1] 表示第 i 天交易完后手里持有一支股票的最大利润(i 从 0 开始)
        # # dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
        # # dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
        # n = len(prices)
        # dp = [[0]*2 for _ in range(n)]
        # dp[0][0] = 0
        # dp[0][1] = -prices[0]
        # for i in range(1, n):
        #     dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
        #     dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
        # return max([x[0] for x in dp])

        # 空间优化
        n = len(prices)
        dp_0 = 0
        dp_1 = -prices[0]
        for i in range(1, n):
            dp_temp = dp_0
            dp_0 = max(dp_0, dp_1 + prices[i])
            dp_1 = max(dp_1, dp_temp - prices[i])
        return dp_0
        
        # 贪心算法,把所有的上坡收集到就是最大利润
        n = len(prices)
        res = 0
        for i in range(1, n):
            if prices[i] - prices[i-1] > 0:
                res += prices[i] - prices[i-1]
        return res

124. 二叉树中的最大路径和

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def __init__(self):
        self.res = float("-inf")
    
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        # 深度优先搜索, 返回当前树能贡献的最大路径值, 路径一定包含 root
        # 注意:在对每个子树进行计算时更新全局最大值
        def dfs(root: Optional[TreeNode]) -> int:
            if not root:
                return 0
            left_max = max(0, dfs(root.left))  # 左子树最大值
            right_max = max(0, dfs(root.right))  # 右子树最大值
            # 更新全局最大值, max(root, left+root, root+right, left+root+right)
            self.res = max(self.res, root.val + left_max + right_max)  # 以当前根节点为树的路径最大值
            # 返回当前根节点可向父节点贡献的路径最大值,可能是 左+根 右+根 根
            # (不能是 左+根+右,与父节点无法形成有效路径)
            return max(root.val + left_max, root.val + right_max)  
        dfs(root)
        return self.res

131. 分割回文串

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        # 回溯 + dp
        # 时间复杂度:O(n*2^n), 空间复杂度 O(n^2)
        res = []
        ans = []
        n = len(s)
        # 构建 dp 数组, dp[i][j]表示 s[i:j+1] 是否回文,(也可采用 memo 存储)
        # i >= j,  dp[i][j] = True
        # i < j, dp[i][j] = dp[i+1][j-1] and s[i] == s[j]
        dp = [[True] * n for _ in range(n)]
        for i in range(n-1, -1, -1):
            for j in range(i+1, n):
                dp[i][j] = dp[i+1][j-1] and s[i] == s[j]

        def isPalindrome(i: int, j: int) -> bool:
            return dp[i][j]

        # 递归截止条件,做选择,递归,退出选择
        def dfs(i: int):
            # 截止条件,索引超限
            if i >= n:
                res.append(ans[:])
                return
            # 做选择
            for j in range(i, n):
                if isPalindrome(i, j):
                    ans.append(s[i:j+1])
                    # 进入递归
                    dfs(j+1)
                    # 退出选择
                    ans.pop()

        dfs(0)
        return res

152. 乘积最大子数组

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        # # dp,与【最大子数组和】不一样,不满足最优子结构
        # # 需要维护两个值,以 nums[i] 结尾的最大值 & 最小值
        # n = len(nums)
        # if n == 0:
        #     return 0
        # dp_max = [0 for _ in range(n)]
        # dp_min = [0 for _ in range(n)]
        # dp_max[0] = nums[0]
        # dp_min[0] = nums[0]
        # for i in range(1, n):
        #     dp_max[i] = max(dp_max[i-1] * nums[i], dp_min[i-1] * nums[i], nums[i])
        #     dp_min[i] = min(dp_min[i-1] * nums[i], dp_max[i-1] * nums[i], nums[i])
        # return max(dp_max)

        # 空间优化
        n = len(nums)
        if n == 0:
            return 0
        dp_max = nums[0]
        dp_min = nums[0]
        res = nums[0]
        for i in range(1, n):
            tmp = dp_max
            dp_max = max(dp_max * nums[i], dp_min * nums[i], nums[i])
            dp_min = min(dp_min * nums[i], tmp * nums[i], nums[i])
            res = max(res, dp_max)
        return res
        

174. 地下城游戏

class Solution:
    def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
        # dp dp[i][j]表示要到达右下角所需要的最低初始值
        # 遍历方向需要从右下角开始,向左向上进行
        # 遇到 dp[i][j] <= 0 时,需要置为 1,保证活着
        m = len(dungeon)
        n = len(dungeon[0])
        dp = [[0]*n for _ in range(m)]
        dp[m-1][n-1] = (-dungeon[m-1][n-1] + 1) if dungeon[m-1][n-1] <= 0 else 1
        for j in range(n-2, -1, -1):
            dp[m-1][j] = max(dp[m-1][j+1] - dungeon[m-1][j], 1)
        for i in range(m-2, -1, -1):
            dp[i][n-1] = max(dp[i+1][n-1] - dungeon[i][n-1], 1)
        for i in range(m-2, -1, -1):
            for j in range(n-2, -1, -1):
                dp[i][j] = max(min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j], 1)
        return dp[0][0]

        # # dfs 暴力求解 方案超时
        # m = len(dungeon)
        # n = len(dungeon[0])

        # # dfs 返回的是 在 (i, j) 位置所需要的最小初始值
        # def dfs(i: int, j: int) -> int: 
        #     if (i == m-1 and j == n-1):
        #         return max(-dungeon[i][j] + 1, 1)
        #     r_min = dfs(i, j+1) if j <= n-2 else float('inf')
        #     d_min = dfs(i+1, j) if i <= m-2 else float('inf')
        #     res = max(min(r_min, d_min) - dungeon[i][j], 1)
        #     # print(i, j, res)  # 打印,判断重复递归
        #     return res

        # return dfs(0, 0)

        # # dfs 暴力求解 方案超时
        # m = len(dungeon)
        # n = len(dungeon[0])
        # # 增加 memo
        # memo = [[float('inf')]*n for _ in range(m)]
        # # dfs 返回的是 在 (i, j) 位置所需要的最小初始值
        # def dfs(i: int, j: int) -> int:
        #     res = 0
        #     if memo[i][j] < float('inf'):
        #         return memo[i][j]
        #     if (i == m-1 and j == n-1):
        #         res = max(-dungeon[i][j] + 1, 1)
        #     else:
        #         r_min = dfs(i, j+1) if j <= n-2 else float('inf')
        #         d_min = dfs(i+1, j) if i <= m-2 else float('inf')
        #         res = max(min(r_min, d_min) - dungeon[i][j], 1)
        #     return res

        # return dfs(0, 0)
        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值