312. 戳气球 (分治、区间 dp)
有 n
个气球,编号为0
到 n - 1
,每个气球上都标有一个数字,这些数字存在数组 nums
中。
现在要求你戳破所有的气球。戳破第 i
个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1]
枚硬币。 这里的 i - 1
和 i + 1
代表和 i
相邻的两个气球的序号。如果 i - 1
或 i + 1
超出了数组的边界,那么就当它是一个数字为 1
的气球。
求所能获得硬币的最大数量。
示例 1:
输入:nums = [3,1,5,8] 输出:167 解释: 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
示例 2:
输入:nums = [1,5] 输出:10
class Solution: def maxCoins(self, nums: List[int]) -> int: def backtrack(start,end): if start+1 >= end: return 0 res = 0 for k in range(start+1,end): tt = backtrack(start,k) + nums[start] * nums[k] * nums[end] + backtrack(k,end) res = max(tt,res) return res nums = [1] + nums + [1] return backtrack(0,len(nums)-1)
class Solution: def maxCoins(self, nums: List[int]) -> int: memo = {} def backtrack(start,end): key = str(start)+'-'+str(end) if key in memo: return memo[key] if start+1 >= end: memo[key] = 0 return 0 res = 0 for k in range(start+1,end): tt = backtrack(start,k) + nums[start] * nums[k] * nums[end] + backtrack(k,end) res = max(tt,res) memo[key] = res return res nums = [1] + nums + [1] return backtrack(0,len(nums)-1)
暴力:
解题思路
我们来看一个区间,这个区间的气球长这样
假设这个区间是个开区间,最左边索引 i,最右边索引 j
我这里说 “开区间” 的意思是,我们只能戳爆 i 和 j 之间的气球,i 和 j 不要戳
DP思路是这样的,就先别管前面是怎么戳的,你只要管这个区间最后一个被戳破的是哪个气球,这最后一个被戳爆的气球就是 k
注意!!!!!
k是这个区间 最后一个 被戳爆的气球!!!!!
假设最后一个被戳爆的气球是粉色的,k 就是粉色气球的索引
然后由于 k 是最后一个被戳爆的,所以它被戳爆之前的场景是什么亚子?
是这样子的朋友们!因为是最后一个被戳爆的,所以它周边没有球了!没有球了!只有这个开区间首尾的 i 和 j 了!!
这就是为什么DP的状态转移方程是只和 i 和 j 位置的数字有关
假设 dp[i][j] 表示开区间 (i,j) 内你能拿到的最多金币
那么这个情况下
你在 (i,j) 开区间得到的金币可以由 dp[i][k] 和 dp[k][j] 进行转移
如果你此刻选择戳爆气球 k,那么你得到的金币数量就是:
total=dp[i][k]+val[i] * val[k] * val[j] + dp[k][j]
class Solution(object): def maxCoins(self, nums): """ :type nums: List[int] :rtype: int """ nums = [1] + nums + [1] n = len(nums) dp = [[0]*n for _ in range(n)] for i in range(n)[::-1]: for j in range(i,n): for k in range(i+1,j): cur = dp[i][k] + nums[i]*nums[k]*nums[j] + dp[k][j] dp[i][j] = max(cur,dp[i][j]) return dp[0][n-1]
class Solution { public: // 开区间,固定住左右,选 中间 int dfs(int start, int end, vector<int>& nums) { if (start+1==end) return 0; int res = 0; for(int i = start+1; i < end;i++) { int tt = dfs(i,end,nums) + dfs(start,i,nums); res = max(res,nums[i]*nums[start]*nums[end] + tt); } return res; } int maxCoins(vector<int>& nums) { nums.insert(nums.begin(),1); //防止i-1<0 越界 nums.push_back(1); //防止j+1>n 越界 return dfs(0,nums.size()-1,nums); } };
class Solution { public: // 开区间,固定住左右,选 中间 int dfs(int start, int end, vector<int>& nums, vector<vector<int>>& dp) { if (start+1==end) return 0; if (dp[start][end]!=-1) return dp[start][end]; int res = 0; for(int i = start+1; i < end;i++) { int tt = dfs(i,end,nums,dp) + dfs(start,i,nums,dp); res = max(res,nums[i]*nums[start]*nums[end] + tt); } dp[start][end] = res; return res; } int maxCoins(vector<int>& nums) { nums.insert(nums.begin(),1); //防止i-1<0 越界 nums.push_back(1); //防止j+1>n 越界 vector<vector<int>>dp(nums.size(),vector<int>(nums.size(),-1)); return dfs(0,nums.size()-1,nums,dp); } };