312. 戳气球

375. 猜数字大小 II

方法一:递归

猜的数字是 k,而答案不是 k 的话,问题变为求解 (1, k - 1) 和 (k + 1, n) 的子问题需要的代价的最大值。

class Solution:
    def getMoneyAmount(self, n: int) -> int:
        @lru_cache(None)
        def dfs(start, end):
            if start >= end: return 0
            # if memo[start][end]: return memo[start][end]
            res = inf
            for k in range(start, end + 1):
                res = min(res, max(dfs(start, k - 1), dfs(k + 1, end)) + k)
            # memo[start][end] = res
            return res
            
			# return min(max(dfs(x, k - 1), dfs(k + 1, y)) + k for k in range(x, y + 1)) if y > x else 0

        # memo = [[0] * (n + 1) for _ in range(n + 1)]
        return dfs(1, n)

方法二:动态规划

1、定义: f(i, j) 表示在范围 [i, j] 内确保获胜的最小现金数,目标是计算 f(1, n)。
2、边界: 当 i ≥ j 时,f(i, j) = 0。
3、状态转移方程:
动态规划 区间_状态转移
4、逆向遍历: 计算子问题的顺序为先计算规模小的子问题,后计算规模大的子问题。

class Solution:
    def getMoneyAmount(self, n: int) -> int:
        f = [[0] * (n + 1) for _ in range(n + 1)]
        for i in range(n - 1, 0, -1):
            for j in range(i + 1, n + 1):
                f[i][j] = min(k + max(f[i][k - 1], f[k + 1][j]) for k in range(i, j))
                
        return f[1][n]