Leetcode 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
思路:
最后一个被戳破的气球,会把整个气球数组分成两部分。
可以用动态规划解决:
定义 dp [ i ] [ j ] :从气球 i 到气球 j 所能达到的最大分数。
设置一个关系 i < k < j , nums[k]是最后一个被戳破的气球。
可以得到状态转移方程:
for( k, k∈(i, j) )
dp[ i ][ j ] = Math.max(dp[ i ][ j ], nums[ i ] * nums[ k ] * nums[ j ] + dp[ i ][ k ] + dp[ k ][ j ]);
nums[ i ] * nums[ k ] * nums[ j ] + dp[ i ][ k ] + dp[ k ][ j ]为,戳完k左边的最大分数 + 戳完k右边的最大分数 + 戳完k的分数
1 class Solution { 2 public int maxCoins(int[] nums) { 3 //给旧数组首尾各添一个不可戳破的1 4 int[] n_nums = new int[nums.length + 2]; 5 for(int i = 0; i < nums.length; ++i){ 6 n_nums[i + 1] = nums[i]; 7 } 8 n_nums[0] = 1; 9 n_nums[n_nums.length - 1] = 1; 10 11 int[][] dp = new int[n_nums.length][n_nums.length]; 12 13 for(int j = 2; j < n_nums.length; ++j){ //j∈[2, n_nums.length),因为j至少比i大2; 14 for(int i = j - 2; i >= 0; --i){ //i∈[0, j - 2] 15 for(int k = j - 1; k > i; --k){ //k∈(i, j) 16 dp[i][j] = Math.max(dp[i][j], n_nums[i] * n_nums[k] * n_nums[j] + dp[i][k] + dp[k][j]); //每一次都是在更新第i个到第j个的最大分数 17 } 18 } 19 } 20 return dp[0][n_nums.length - 1];//返回第0个到最后一个的最大分数 21 } 22 }
时间复杂度(n ^ 3);
空间复杂度(n ^ 2);