打破气球所能获得的最大积分 Burst Balloons

本文探讨了通过爆破气球获取最大收益的问题。利用动态规划和分治法结合备忘录技术,提供了两种解决方案。一种是使用动态规划从底部向上解决问题,另一种是从顶部到底部递归地拆分问题,并使用备忘录避免重复计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:

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: 
(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

Given [3, 1, 5, 8]

Return 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

解决:

【题意】给定n个气球。每次你可以打破一个,打破第i个,那么你会获得nums[left] * nums[i] * nums[right]个积分。 (nums[-1] = nums[n] = 1)求你可以获得的最大积分数。

① https://segmentfault.com/a/1190000007297715

动态规划。dp[i][j]表示打爆区间[i,j]中的所有气球能得到的最多金币。DP的思想是bottom up

最后的剩下一个气球为i的时候,可以获得的分数为:nums[-1]*nums[i]*nums[n].

那么介于i , j之间的k,有: 

dp[i][j] = max(dp[i][j], nums[i - 1] * nums[k] * nums[j + 1] + dp[i][k - 1] + dp[k + 1][j])

( i ≤ k ≤ j ),O(N^2), O(N^2)

class Solution { //15ms
    public static int maxCoins(int[] nums) {
        int[] tmpnums = new int[nums.length + 2];
        System.arraycopy(nums,0,tmpnums,1,nums.length);
        tmpnums[0] = 1;
        int len = tmpnums.length;
        tmpnums[len - 1] = 1;
        int[][] dp = new int[len][len];
        for (int l = 2;l < len;l ++){
            for (int i = 0;i < len - l;i ++){
                int j = i + l;
                for (int k = i + 1;k < j;k ++){
                    dp[i][j] = Math.max(dp[i][j],tmpnums[i] * tmpnums[k] * tmpnums[j] + dp[i][k] + dp[k][j]);
                }
            }
        }
        return dp[0][len - 1];
    }
}

② D&C + Memorization,O(N^3) , O(N^2)

memorization是from top to bottom,每一次都将值分成两部分,然后分别计算。对于在left和right中间的位置i,考虑[left,i]和[i,right]能组成的部分,分别对这两部分进行递归求值。用二维数组记录已经计算过的值,避免重复计算。

class Solution { //10ms
    public static int maxCoins(int[] nums) {
        int[] tmpnums = new int[nums.length + 2];
        for (int i = 1;i <= nums.length;i ++){
            tmpnums[i] = nums[i - 1];
        }
        tmpnums[0] = 1;
        tmpnums[tmpnums.length - 1] = 1;
        int len = tmpnums.length;
        int[][] memo = new int[len][len];
        return burst(tmpnums,0,len - 1,memo);
    }
    public static int burst(int[] nums,int i,int j,int[][] memo){
        if (i + 1 == j) return 0;//[i,j]之间没有ballon可以炸掉
        if (memo[i][j] > 0){//如果已经计算过了,则直接返回
            return memo[i][j];
        }
        int res = 0;
        for (int k = i + 1;k < j;k ++){
            res = Math.max(res,nums[i] * nums[k] * nums[j] + burst(nums,i,k,memo) + burst(nums,k,j,memo));//递归获取左右窗口的最大值
        }
        memo[i][j] = res;
        return res;
    }
}

转载于:https://my.oschina.net/liyurong/blog/1593682

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值