312. Burst Balloons

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 and 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

一道DP题目,我对动态规划的理解还仅停留在0-1背包问题,理所当然的这题目我连别人的解析都看不懂,花了很长时间终于明白了,这里记录下来,看不懂https://leetcode.com/problems/burst-balloons/discuss/76230/C++-dp-detailed-explanation的童鞋可以看一看我这个更啰嗦的版本:)

1.这道题目里难点之一是选什么做状态值?dp[left][right]代表的是子序列nums[left]...nums[right]能获得的最大硬币数

2.如果能想到第1点,根据题意,就能写出dp矩阵的初始值,具体请看下面的演算过程(以[3,1,5,8]为例),很明显dp应该是一个(num.size()+2) * (num.size()+2)的矩阵,初始值如下:


然后我们先来更新对角线上的值,也就是dp[index][index],0<=index<=n-1。以dp[2][2]为例,其表示nums[2]...nums[2]序列能获得的最大硬币数。

[3,1,{5},8],{}中表示这次选中的子序列,很明显,dp[2][2] = 1*5*8 = 40,因为我们只能把编号为5的气球打破。于是我们得到了下述dp矩阵值:


3.接下来又是一个难点:如何计算剩余的dp值,如何确定状态转移方程,因为我们的目标是得到右上角的dp[0][3]值。

先给出状态转移方程:

     dp[left][right] = max(dp[left][right], nums[left-1]*nums[k]*nums[right+1] + dp[left][k-1] + dp[k+1][right]);
对于这个状态转移方程,我们还是举例子说明如何计算dp[i][j],i<j.以dp[0][1]为例,即[{3,1},5,8],现有两种可能:
1.3是最后一个被打破的气球,此时3旁边的数字是1和5,也就是nums[i-1]与nums[j+1],这种选择方案的分数是3*1*5+1*3*5 = 30,其中3*1*5是打破编号为1的气球所得的分数,注意,这也是dp[1][1]的值;2.1是最后一个被打破的气球,此时1旁边的数字是1和5,这种选择方案的分数是 1*3*1 + 1*1*5 = 8,其中1*3*1是dp[0][0]的值总结一下,我们发现dp[1][1] = dp[k+1][right],dp[0][0] = dp[left][k-1],这样就可以对状态转移方程有一个直观的理解了:计算dp[i][j]的思路是依次假定i到j中一个气球是最后打破的,比如说k(k在i到j之间)是最后打破的,那么在这个假定成立的前提下,最大收益是
nums[i-1]*nums[k]*nums[j+1] + dp[i][k-1] + dp[k+1][j]
按照对角线的方向,依次我们可以计算出:


4.如果从nums数组的角度上来看,实际上dp数组的计算是一个从左到右滑动的,并且不断增大的滑动窗口,如下所示:

[{3},1,5,8]...[3,1,5,{8}],[{3,1},5,8]...[3,1,{5,8}],[{3,1,5},8]......

最后贴上程序,DP的题目思路有了就非常简单了。

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        vector<vector<int>> dp(nums.size(), vector<int>(nums.size(), 0));
        for (int len = 1; len <= n; ++len)
        {
            for (int left = 1; left <= n - len + 1; ++left) 
            {
                int right = left + len - 1;
                for (int k = left; k <= right; ++k)
                    dp[left][right] = max(dp[left][right], nums[left-1]*nums[k]*nums[right+1] + dp[left][k-1] + dp[k+1][right]);
            }
        }
        return dp[1][n];
    }
};



猜你喜欢

转载自blog.csdn.net/qq_21602549/article/details/80493089