LeetCode 312. Burst Balloons

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

 

转载于:https://www.cnblogs.com/rookielet/p/10478061.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值