题目
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
我的想法
感觉这道题必须枚举才能做出来,所以用的backtrack,结果是对的,但是超时
class Solution {
public int maxCoins(int[] nums) {
List<Integer> ls = new LinkedList<>();
for(int i : nums){
ls.add(i);
}
return maxHelper(ls);
}
private int maxHelper(List ls) {
int max = 0;
for(int i = 0; i < ls.size(); i++) {
List<Integer> list = new LinkedList<>(ls);
int left = i-1 >= 0 ? list.get(i-1) : 1;
int right = i+1 < list.size() ? list.get(i+1) : 1;
int remove = list.remove(i);
max = Math.max(remove*left*right + maxHelper(list), max);
}
return max;
}
}
感觉其实也可以用DP做的
解答
leetcode solution 1: DP-Top down
非常聪明的写法
[start, ... i - 1, (i), i + 1 ... end] //e.g. [2,3,1,7,5,8]
start,end,i
表示的是待扎气球的位置编号,在例子中此时start
是0
,该气球的数值为2
dp[start][end]
表示扎完在start
到end
之间的泡泡最多能获得大的数。
在上面的例子中
dp[0][5]
表示扎完所有的泡泡能获得的最大数
dp[1][3]
表示扎完位置为1,2,3
的气球(对于数字为3,1,7
)能获得的最大数
在i
被扎破前,i
的左边[start,i-1]
和右边[i+1,end]
中的所有泡泡一定已经全部扎破了。但是其外边界start-1,end+1
一直存在。
因此,可以得到扎破气球i
可获得的总数值为
val = dp[start, i - 1] //左边已扎泡泡
+ nums[i] * nums[start - 1] * nums[end + 1] //扎完i可新获得的数值
+ dp[i + 1, end] //右边已扎泡泡
e.g.
1[1,2,3,4,5,6,7,8,9]1. //两个1为外边界
i = 3 //假设此时i=3,即数值为4的气球
start = 0, end = 8 //边界
val = dp[0,2] + dp[4,8] //假设dp[0,2]已知,而dp[3,8]待求
+ 1 * 4 * 1 //此时外边界-1和9在数组外,为1
//求dp[3,8]
4[5,6,7,8,9]1
i = 6 //假设此时i=6,即数值为7的气球
val = dp[4,5] + dp[7,8] //假设dp[4,5]dp[7,8]都已知
+ 4 * 7 * 1 //此时左边的外边界为3号气球(此时还未戳破)
public class Solution {
public int maxCoins(int[] nums) {
int[][] dp = new int[nums.length][nums.length];
return maxCoins(nums, 0, nums.length - 1, dp);
}
public int maxCoins(int[] nums, int start, int end, int[][] dp) {
if (start > end) {
return 0;
}
if (dp[start][end] != 0) {
return dp[start][end];
}
int max = nums[start];
for (int i = start; i <= end; i++) {
int val = maxCoins(nums, start, i - 1, dp) +
get(nums, i) * get(nums, start - 1) * get(nums, end + 1) +
maxCoins(nums, i + 1, end, dp);
max = Math.max(max, val);
}
dp[start][end] = max;
return max;
}
public int get(int[] nums, int i) {
if (i == -1 || i == nums.length) {
return 1;
}
return nums[i];
}
}
leetcode solution 2: DP-Bottom up
public class Solution {
public int maxCoins(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int[][] dp = new int[nums.length][nums.length];
for (int len = 1; len <= nums.length; len++) {
for (int start = 0; start <= nums.length - len; start++) {
int end = start + len - 1;
for (int i = start; i <= end; i++) {
int coins = nums[i] * getValue(nums, start - 1) * getValue(nums, end + 1);
coins += i != start ? dp[start][i - 1] : 0;
// If not first one, we can add subrange on its left.
coins += i != end ? dp[i + 1][end] : 0;
// 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];
}
}