312. 戳气球 (分治、区间 dp)

 

labuladong 题解思路
难度困难

有 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);
    }
};

 

posted @ 2022-08-07 16:02  乐乐章  阅读(29)  评论(0编辑  收藏  举报