312. Burst Balloons

入手的时候一直想打破表面,发现数学本质,结果并没有发现什么数学本质,直接去找答案。

挺复杂的,一般H的题如果我面试的时候遇到,用成语形容可能出现的情况,就是我可能会吃屎。这题对我来说是一定会吃屎

dietpepsi给的答案很详细。

首先,一排气球左边是L,右边是R,我们可以假设最后一个打爆的气球是i,那倒数第二个打爆的气球j就出现在L-i或者i-R.

dp[L][R] = nums[i] * nums[L] * nums[R] + dp[L][i]+dp[i][R];

dp[L][R]表示从L打到R的最终得分。
nums[i] * nums[L] * nums[R]表示最后一次得分,这里我们已经假设L到i之间,i到R之间气球已经都打爆了。
刚才假设的内容,L到I之间,I到R之间都已经打爆了,但是我们没有加分,所以要加上dp[L][i]+dp[i][R],其实就是最后一个气球之前的气球。

DP[L][R]的时候要遍历,看看最后打哪个最合算,有很多重复计算,可以用DP记住。

dietpepsi的解释里面做了很多假设,看完之后我真射了。

题很难,如果不是碰到原题,面试遇到只能认栽。个人感觉更重要的是dietpepsi提供了很详细地思考过程,怎么一步一步思考找到答案,如何从n!到n^3。值得一看,可能比做出这一道题更重要:
(https://discuss.leetcode.com/topic/30746/share-some-analysis-and-explanations)


public class Solution 
{
    public int maxCoins(int[] oldnums) 
    {
        int[] nums = new int[oldnums.length+2];
        int index = 1;
        nums[0] = 1;
        for(int i: oldnums) nums[index++] = i;
        nums[index] = 1;
        
        int[][] dp = new int[nums.length][nums.length];
        
        return helper(nums,0,nums.length-1,dp);
    }
    
    public int helper(int[] nums, int left, int right, int[][] dp)
    {
        if(dp[left][right]!=0) return dp[left][right];
        
        int res = 0;
        
        for(int i = left+1;i < right;i++)
        {
            res = Math.max(dp[left][right],
                                helper(nums,left,i,dp)+
                                helper(nums,i,right,dp)+
                                nums[i]*nums[left]*nums[right]);
                                
            dp[left][right] = res;
        }
        
        
        return res;
    }
    
}

还有个更DP的做法,没理解,二刷再说。



二刷

二刷还是做晕了,今天状态好差,这几个H难度的题都只是理解做法了,但是并没有归纳总结出什么general的DP思想,DP对我来说千变万化。。。

这个题必须这么考虑。

Left score1 score2 score3 .... scoreN Right
假设有这么多气球,最边上2个left, right是1.

假设此时就剩1个气球,let's say its score3.

那么这最后一轮的得分是score3 * 他左边 * 他右边, 因为它是最后一个,他左右是Left和Right,都是1. 最后一轮得分,score3

倒数第二轮的情况就不一定了,他可能是是在(Left, score3)这个范围内,也可能在(left3, Right)这个范围内。
但是有一点肯定,不管是score3左边的区间,还是右边的区间,只要有气球被打爆,就加分。。 所以要加上递归那2个区间的结果。。
当前得分 + 左边区间得分 + 右边区间得分

在每次进入递归,然后算此区间打哪个气球最合算。

这里“打哪个气球”,是假设打的这个气球是此去见的最后一个,所以都要乘以当前区间的左右边界,而不是相邻的2个气球。

这里一开始搞乱了。。

public class Solution {
    public int maxCoins(int[] nums) {
        if (nums.length == 0) return 0;
        
        int len = nums.length;
        int[] scores = new int[len+2];
        
        scores[0] = 1;
        scores[len + 1] = 1;
        
        for (int i = 0; i < len; i++) {
            scores[i+1] = nums[i];
        }
        int[][] mem = new int[len+2][len+2];
        
        return calScore(1, len, scores, mem);
    }
    
    public int calScore(int l, int r, int[] scores, int[][] mem) {
        if (mem[l][r] != 0) return mem[l][r];
        int tempMax = 0;
        for (int i = l; i <= r; i++) {
            int leftScore = calScore(l, i-1, scores, mem);
            int rightScore = calScore(i+1, r, scores, mem);
            int tempScore = scores[l-1] * scores[i] * scores[r+1];
            tempMax = Math.max(tempMax, leftScore + rightScore + tempScore);
        }
        mem[l][r] = tempMax;
        return tempMax;
    }
}
posted @ 2016-12-04 13:32  哇呀呀..生气啦~  阅读(288)  评论(0编辑  收藏  举报