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);

 

posted @ 2019-08-04 20:20  led二极管  阅读(250)  评论(0编辑  收藏  举报