312题-戳气球
题目
有n个气球,编号为0到n-1,每个气球上都标有一个数字,这些数字存在数组nums中。现在要求你戳破所有的气球。如果你戳破气球 i ,就可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。
求所能获得硬币的最大数量。
说明:
- 你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的所以并不能被戳破。
- 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
示例:
输入: [3,1,5,8] 输出: 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
解答
这里我打算采用动态规划方法,但是我们发现,当存在四个以上的数的时候,在取掉中间的数时,根据题目要求并不是分为两部分,而是合并到一起形成一个新数组,所以子问题不独立。所以我们需要换一种方式进行状态转移。在区间i-j中,我们寻找最后戳破的气球是得dp[i][j]最大。这里和前面我们选择最先戳破中间k不同。因为k是最后戳破的,所以i到k-1和k+1到j仍然是独立的子问题。状态转移方程为:dp[left][right] = dp[left][i]+dp[i][right]+arr[left] * arr[i] * arr[right]。
代码
class Solution {
public int maxCoins(int[] nums) {
int len = nums.length+2;
int[] arr = new int[len];
System.arraycopy(nums,0,arr,1,nums.length);
arr[0] = 1;
arr[len-1] = 1;
int[][] dp = new int[len][len];
for(int gop = 2; gop <len; gop++){
for(int left = 0; left<len-gop; left++){
int right = left+gop;
int conis = 0;
for(int i = left+1; i < right;i++){
conis = Math.max(conis,dp[left][i]+dp[i][right]+arr[left]*arr[i]*arr[right]);
}
dp[left][right] = conis;
}
}
return dp[0][len-1];
}
}