Problem Description:
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
一共有n个气球,编号从0到n-1。气球上印有数字,用数组nums[]记录。要求爆炸掉所有的气球,每次爆炸时得到金币的数量为nums[left] * nums[i] * nums[right]
coins,left和right是i的左右邻近的气球序号。爆炸后,left和right的气球相邻。选择爆炸的顺序使得得到的coins最多。
解题思路:
首先想到的是回溯,需要爆炸n个气球,每次随机选择一个气球爆炸,则复杂度为O(n!),可能只在n<12的情况下能通过。
然后想到的是贪心。想到的是每次爆炸掉最小的气球,然后将数组聚拢,则第i次爆炸的时间复杂度为O(n - i),最终复杂度为O(n^2)。这样感觉挺快的,提交后发现这是错误的策略。
最后想到的是动态规划:先在原数组两侧增加两个1,将新数组记为arr,长度为n。使用二维数组dp[][]来记录爆炸得到的coin数,dp[left][right]代表爆炸掉(left, right)范围(不包含两侧)的所有气球的最大coin数。用k表示连续爆炸的长度,即k = right - left + 1,让k从3到n,对于每个k求出来所有可能的dp[left][right];对于每一个k,有0到n-k共n-k+1个dp[left][right]要求;对于每一个dp[left][right],在i处将其划分,i可以取从left+1到right-1, dp[left][right] = max(dp[left][right], dp[left][i] + dp[i][right] + arr[i] * arr[left] * arr[right])(left到i中间(不包含)已经爆炸,i到right同)。
代码如下
class Solution { public int maxCoins(int[] nums) { int[] arr = new int[nums.length + 2]; for(int i = 0; i < nums.length; i++) { arr[i + 1] = nums[i]; } arr[0] = arr[arr.length - 1] = 1; int[][] dp = new int[arr.length][arr.length]; for(int k = 3; k <= arr.length; k++) { for(int left = 0; left <= arr.length - k; left++) { int right = left + k - 1; for(int i = left + 1; i < right; i++) { dp[left][right] = Math.max(dp[left][right], arr[left] * arr[i] * arr[right] + dp[left][i] + dp[i][right]); } } } return dp[0][arr.length - 1]; } }
另有自顶向下的带memo的代码如下
class Solution { public int maxCoins(int[] nums) { int[] arr = new int[nums.length + 2]; for(int i = 0; i < nums.length; i++) { arr[i + 1] = nums[i]; } arr[0] = arr[arr.length - 1] = 1; int[][] memo = new int[arr.length][arr.length]; return burst(memo, arr, 0, arr.length - 1); } public int burst(int[][] memo, int[] arr, int left, int right) { if(left + 1 >= right) return 0; if(memo[left][right] > 0) return memo[left][right]; int res = 0; for(int i = left + 1; i < right; i++) { res = Math.max(res, arr[left] * arr[i] * arr[right] + burst(memo, arr, left, i) + burst(memo, arr, i, right)); } memo[left][right] = res; return res; } }