[Leetcode]312. Burst Balloons

312. Burst Balloons

  原题目链接
  题目中文释义
  这是一道很好的DP题目,但又不同于传统的DP,以下将自己的思路和别人的想法结合,总结成文。

最初思路——递归

  最开始拿到这道题目的时候,想到的最简单粗暴的方法就是递归。完成此题目总共需要n步(戳破n个气球)第一次有n种方式戳破气球,第二次有n-1种方式……以此类推,算出每一种戳气球的方式所得的金币,然后返回最大值。这样的总共有n!种方式,时间复杂度为O(n!),显然是我们无法接受的。所以逐步优化。

DP引入

  接下来可以这样考虑:如果我们设剩余的气球数为N,已经戳破的气球数为M。我们可以发现maxCoin(N)与M是无关的。也就是已经戳破的气球不会影响后面气球所能得到的最大值。这可以让我们想到DP中的无后效性。于是我们可以尝试DP(bottom up),即先计算戳破2个气球的maxCoin,再计算戳破3个气球的maxCoin,以此类推知道我们算出n个气球的maxCoin(重叠子问题)。这种方法比O(n!)好但并不优于O(2^n),我们需要进一步改进寻找多项式级别的算法。

最终解法

  有了之前的想法,我们接下来准备用DP对问题进一步优化。一般而言,我们习惯性的从中选出一个数,然后算出剩余数字所能得到的最大值,这样可以得到与原问题相似的子问题。
  大概的公式如下:
    所能得到的最大分数 = max(取出一个数字的分数+余下数列所能得到的最大分数)
  这是我们一般的DP思路,一般而言通过循环遍历就可以依次得出子问题的答案,从而最终获得正确答案。但当我们这样尝试的时候马上就会发现问题:取出的数字得分与左右两个相邻数字有关,同时任意操作会改变左右的相邻关系
  我们进一步阐述一下这个问题:
  比如你有一个数列[a1,a2,a3...an],你想通过选择一个点k将其分为两部分,我们设为N1,N2。我们戳破k所在的气球,将得到N1 = [a1,a2,...,ak-1]N2 = [ak+1,ak+2,...,an]。问题在于,根据计算规则,k气球破了之后,a(k-1)和a(k+1)会变成相邻的,如果此时踩a(k-1)或者a(k+1),则都会受到另一个子整体的影响。换言之,我们分割出来的两个子问题不是相互独立的,所以这样分割是行不通的。但DP的方向还是没错的,我们接下来的任务是确定k所在的位置,从而使分出来的两个子问题可以相互独立,从而进一步确定转移方程。
  这里的关键思想在于:如果数列中只有一个数,或者说最后只剩下一个数(a[k])时,我们是知道它的分数的:
           1*a[k]*1 或者 nums[-1]*a[k]*nums[n]
  如果说我们设k是最后一个被戳破的气球,那么在这之前,N1 = [a1,a2,...,ak-1]N2 = [ak+1,ak+2,...,an]就有了非常明确的边界,也就实现了两者相互独立的目标。
这样,我们得出的转移方程如下:
  dp[left][right]=max(dp[left][right],nums[left]*nums[k]*nums[right]+dp[left][k]+dp[k][right])

代码

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        vector<int> temp(nums.size()+2,0);
        for(int i = 0; i < nums.size(); i++)
            temp[i+1] = nums[i];
        temp[0] = temp[nums.size()+1] = 1;
        int n = temp.size();
        vector<vector<int>> dp(n,vector<int>(n,0));
        for(int d = 2; d < n ; d++){
            for(int left = 0; left < n-d; left++){
                int right = left + d;
            for(int i = left+1; i < right; i++)
                dp[left][right] = max(dp[left][right], temp[left]*temp[i]*temp[right]+dp[left][i]+dp[i][right]);
            } 
        }
        return dp[0][n-1];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值