[leetcode] 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: 
(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

Given [3, 1, 5, 8]

Return 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


这题的分治思想并不是很直观,因为divide并不是可以一次性地将一个问题分成两个子问题。(我们应该跳出分治一定是分成两个子问题的这种思维定势),而且conquer的时候也不是常规意义的合并,而是取max。

不过直接采用递归的思想去思考,还是比较容易的。但写算法的话就可以用DP了,递归会超时。
dp(i, j) = max{dp(i, k) + dp(k, j) + nums[i]*nums[k}*nums[j]}, k = [i+1, j-1]
dp(i,j)表示数组arr[i,j]对应的该问题的最优解,其中arr[i]和arr[j]都是不能被burst的气球。
而上面的递归式右边max大括号里的表达式表示,最后一个burst的气球是第k个时,我们能够得到的最多的硬币的数目。

另外,这里填充dp的方式也不是常规的i从0到n-1,j也从0到n-1,而是按照j与i之间的间隔逐渐增大的对角线的方式进行填充。至于为什么要用这种方式填充,上面的递推式给出了答案。我们要求dp(i,j)时,一定要预先知道i和j之间的任意区间的dp值,而我们如果采用i从0到n-1,j也从0到n-1或者由大到小的迭代方式都没法满足这个条件,即如果i从小到大,j也从小到大,那么求dp(i,j)时,需要用到dp(i,k)和dp(k,j),i<k<h,其中dp(i,k)会在它之前得到,但dp(k,j)就还没计算到,因为k>i。
而经过观察我们发现,dp(i,k)和dp(k,j)的下标区间长度一定比dp(i,j)要小,所以我们可以采用区间长度由小到大的方式进行填充dp数组。

问题想明白了的话,其实代码还是相对比较容易写的。
class Solution {
public:
    int maxCoins(vector<int>& nums) {
        if(0 == nums.size()) return 0;
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        
        int n = nums.size();
        vector<int> col(n, 0);
        vector<vector<int>> dp(n, col);
        
        for(int len = 2; len < n; ++len)
        {
            for(int i = 0; i < n-len; ++i)
            {
                int ret = 0;
                for(int k = i+1; k < i+len; ++k)
                {
                    int tmp = dp[i][k] + dp[k][i+len] + nums[i]*nums[k]*nums[i+len];
                    if(tmp > ret) ret = tmp;
                }
                dp[i][i+len] = ret;
            }
        }
        return dp[0][n-1];
    }
};





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值