题目
直觉
戳破气球时,不会影响其他气球的得分,且列表长度始终在减少。这意味着我们可以用动态规划来解决这道题。
思路
考虑这样一种定义:$dp[i][j]$ 表示戳破 i 到 j 之间所有气球所能获得的最大得分。在这种情况下,如果 n 是列表长度,那么答案就是 $dp[0][n-1]$。
现在来思考如何求得 $dp[i][j]$。正如我们在直觉部分提到的,气球数量始终在减少,这意味着我们可以先计算较短区间的结果,再逐步扩展区间到 $0 \to n-1$。
由此得出我们的解法:
首先,遍历区间长度。
接着,将区间 $i \to j$ 拆分为两个子区间 $i \to k-1$ 和 $k+1 \to j$。
然后,将子区间 $i \to k-1$、$k+1 \to j$ 的得分相加,再加上戳破第 k 个气球的得分,即可计算出区间 $i \to j$ 的总得分。
由于子区间 $i \to k-1$ 和 $k+1 \to j$ 中的气球已经全部被戳破,所以戳破第 k 个气球的得分增量为 $nums[k] \times nums[i-1] \times nums[j+1]$。
最后需要注意子区间边界的处理:由于当前戳破的气球可以是区间 $i \to j$ 的边界,因此 k 需要在 $[i, j]$ 上闭区间遍历。
最终答案即为 $dp[0][n-1]$。
复杂度
- 时间复杂度:$O(N^3)$,N 为列表长度。
- 空间复杂度:$O(N^2)$,N 为列表长度。
代码
class Solution:
def maxCoins(self, nums: List[int]) -> int:
n = len(nums)
nums = nums + [1]
dp = [[0 for _ in range(n+1)] for _ in range(n+1)]
for i in range(n):
dp[i][i] = nums[i] * nums[i-1] * nums[i+1]
for i in range(1, n):
for l in range(n):
if i + l == n:
break
r = i + l
for k in range(l, r+1):
dp[l][r] = max(dp[l][r], dp[l][k-1] + dp[k+1][r] + nums[k] * nums[l-1] * nums[r + 1])
# print(dp)
return dp[0][n-1]
