【LeetCode】312. Burst Balloons 解題報告(Python)
題目描述:
Given n
balloons, indexed from 0
to n-1
. Each balloon is painted with a number on it represented by array nums
. You are asked to burst all the balloons. If the you burst balloon i
you will get nums[left] * nums[i] * nums[right]
coins. Here left
and right
are adjacent indices of i
. After the burst, the left
right
then becomes adjacent.
Find the maximum coins you can collect by bursting the balloons wisely.
Note:
- You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
- 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
Example:
Input: [3,1,5,8] Output: 167 Explanation: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
題目大意
打氣球遊戲,當我們打某個位置的氣球的時候,能獲得它左右兩個氣球上的分數和自身分數的乘積。問如何打氣球能獲得最多的分數?可以認為最左右兩邊隱含著分數為1不用打破的氣球。
解題方法
這個是個DP的題目,當然也可以通過記憶化搜尋的方式解決。
令dfs(i, j) 和 c[i][j]是在第[i, j]閉區間上打破氣球能獲得最大值。那麼,在其中找到一個不打破的氣球k,則可以得到以下關係:
c[i][j] = max(c[i][j], self.dfs(nums, c, i, k - 1) + nums[i - 1] * nums[k] * nums[j + 1] + self.dfs(nums, c, k + 1, j))
含義是,我們找出在[i, k - 1]、[k + 1, j]閉區間打氣球的分數最大值,然後會把第i - 1和第j + 1個氣球保留下來,讓這兩個氣球和第k個氣球相乘,最後求三個加法。
模擬左右兩邊的氣球的方法是直接新增上首尾各一個1,同時使用記憶化能加速不少,也為下一步的DP提供思路。
時間複雜度是O(N^2 * log(N))(不會算…),空間複雜度是O(N)。
class Solution(object):
def maxCoins(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
nums.insert(0, 1)
nums.append(1)
c = [[0] * (n + 2) for _ in range(n + 2)]
return self.dfs(nums, c, 1, n)
def dfs(self, nums, c, i, j):
if i > j: return 0
if c[i][j] > 0: return c[i][j]
if i == j: return nums[i - 1] * nums[i] * nums[i + 1]
res = 0
for k in range(i, j + 1):
res = max(res, self.dfs(nums, c, i, k - 1) + nums[i - 1] * nums[k] * nums[j + 1] + self.dfs(nums, c, k + 1, j))
c[i][j] = res
return c[i][j]
第二種解法是使用DP。
DP一般都可以通過記憶化搜尋來改出來,但是我不會。。很遺憾,參考了別人的程式碼,還是沒搞懂。。
class Solution(object):
def maxCoins(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
nums.insert(0, 1)
nums.append(1)
dp = [[0] * (n + 2) for _ in range(n + 2)]
for len_ in range(1, n + 1):
for left in range(1, n - len_ + 2):
right = left + len_ - 1
for k in range(left, right + 1):
dp[left][right] = max(dp[left][right], dp[left][k - 1] + nums[left - 1] * nums[k] * nums[right + 1] + dp[k + 1][right])
return dp[1][n]
參考資料:
日期
2018 年 10 月 2 日 —— 小藍單車莫名其妙收了我1塊錢,明明每個月免費騎10次的啊!