leetcode 312. Burst Balloons

来源:互联网 发布:linux 中文方格 编辑:程序博客网 时间:2024/06/06 09:35

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

这道题考虑的角度很重要。

我们假设在 [left,right] 区间内索引为 i 的气球被最后戳破,那么maxcoins(left,right) =nums[left-1]*nums[i]*nums[right+1] + maxcoins(left,i-1) +maxcoins(i+1,right).

以 [3,2,5,8] 为例子吧,假设2被最后戳破,那么maxcoins(0,3)=1*2*1 + maxcoins(0,0) +maxcoins(2,3),
因为最后戳破的是2,那么最后2左右已经没有气球了,所以最终的那次乘积只有2乘以(索引为-1的元素)乘以(索引为n的元素)。
然后递归地看2左边的元素区间[3]:maxcoins(0,0)=nums[0]*nums[left-1]*nums[right+1]+0+0=1*3*2=6,此时left-1指索引为-1的元素,right+1就是指2了。“+0+0"是因为更进一步的递归已经使right<left了,所以返回0
然后递归地看2右边的元素区间[5,8]:再假设该区间内5是最后被戳破的,它的left-1和right+1分别是2和1。我们可以想象8先被戳破之后,5左边是2,右边就变成了1,符合逻辑。......再假设该区间内8是最后被戳破的,它的left-1和right+1分别是2和1。我们可以想象5先被戳破之后,8左边变成了2,右边是1,也符合逻辑。

另外,这道题需要在递归时保存一个记忆数组memory[left][right]来记忆index为[left,right]的区间内maxCoins值,不然递归中会有多次重复计算[left,right]区间的最大硬币数,会TLE。

package leetcode;public class Burst_Balloons_312 {public int maxCoins(int[] nums) {int[][] memory=new int[nums.length][nums.length];return maxCoins(nums,0,nums.length-1,memory);}public int maxCoins(int[] nums,int left,int right,int[][] memory){if(left>right){return 0;}if(memory[left][right]>0){return memory[left][right];}int maxCoins=0;for(int i=left;i<=right;i++){int coins=getValue(nums, left-1)*getValue(nums, i)*getValue(nums, right+1)+maxCoins(nums,left,i-1,memory)+maxCoins(nums, i+1, right,memory);if(coins>maxCoins){maxCoins=coins;}}memory[left][right]=maxCoins;return maxCoins;}public int getValue(int[] nums,int index){if(index==-1||index==nums.length){return 1;}else{return nums[index];}}public static void main(String[] args) {// TODO Auto-generated method stubBurst_Balloons_312 b=new Burst_Balloons_312();int[] nums=new int[]{8,2,6,8,9,8,1,4,1,5,3,0,7,7,0,4,2,2};System.out.println(b.maxCoins(nums));}}

这道题还可以用DP来代替递归。dp[i][j] 来存储 index为i~j 的区间内的最大硬币数。那么我们需要的结果就是dp[0][nums.length - 1].
这道题的DP,并不是跟以前一样 i和j从小到大来填充,大的i、j 值通过 小的i、j值算出来填充。这道题这样想的话就会没有解决办法。
思路是从 j-i 的范围从小到大来填充。一开始考虑i=j的情形,填充。再考虑j-i=1的情形,填充。......慢慢把范围扩大,最后扩大成 j-i=nums.length - 1来填充,这就是我们要的结果。

即       [ ][ ][ ][ ][ ][ ]
     => [    ][    ][    ]
     => [       ][       ]
     => [                ]

因为在求解left~right的maxcoins过程中,假设当前最后被戳破的是i,那么之后要递归求解(left,i-1)和(i+1,right)的情况,也就是说要用到范围更小的区间。所以范围更小的区间需要先被求出来。

哈哈,要是还看不懂的就看看别人的英文原版解释吧。
The basic idea is that we can find the maximal coins of a subrange by trying every possible final burst within that range. Final burst means that we should burst balloon i as the very last one and burst all the other balloons in whatever order. dp[i][j] means the maximal coins for range [i...j]. In this case, our final answer is dp[0][nums.length - 1].

When finding the maximal coins within a range [start...end], since balloon i is the last one to burst, we know that in previous steps we have already got maximal coins of range[start .. i - 1] and range[i + 1 .. start], and the last step is to burst ballon i and get the product of balloon to the left of i, balloon i, and ballon to the right of i. In this case, balloon to the left/right of i is balloon start - 1 and balloon end + 1. 

In my opinion, subrange will  be used by a larger range when we're trying for every possible final burst. It will be like [larger start.....start - 1, [start .. end] end + 1/ larger end], when final burst is at index start - 1, the result of this sub range will be used, and at this moment, start - 1 will be there because it's the final burst and end + 1 will also be there because is out of range. Then we can guarantee start - 1 and end + 1 will be there as adjacent balloons of balloon i for coins. 

public int maxCoins(int[] nums) {if (nums == null || nums.length == 0)return 0;int[][] dp = new int[nums.length][nums.length];for (int len = 0; len < nums.length; len++) {for (int start = 0; start < nums.length - len; start++) {int end = start + len;for (int i = start; i <= end; i++) {int coins = nums[i] * getValue(nums, start - 1) * getValue(nums, end + 1);if (i != start) {coins += dp[start][i - 1];// If not first one, we can// add subrange on its left.}if (i != end) {coins += dp[i + 1][end];// If not last one, we can add// subrange on its right}dp[start][end] = Math.max(dp[start][end], coins);}}}return dp[0][nums.length - 1];}private int getValue(int[] nums, int i) { // Deal with num[-1] and// num[num.length]if (i < 0 || i >= nums.length) {return 1;}return nums[i];}

原创粉丝点击