leetcode312戳气球

戳气球

回溯

第一眼看到题目肯定还是先把暴力回溯解写出来看看到底是个什么名堂。很明显,按照暴力解法,就是一个类似于求子集的题目,中间增加了一些步骤也就是计算当前硬币数量。具体步骤如下:

  1. 遍历每一个气球,表示要戳破他,然后计算当前的硬币数量。
  2. 遍历后去最大的值为计算结果。

其中的trick:因为硬币数量大于1,因此可以把戳破的气球设置为-1。而计算当前硬币结果的代码如下(注意,是分别求左右边界,而不是同时去求!):

    private int computeCurCoin(int[] nums, int center){
        int[] ans ={1,nums[center],1};
        int left = center-1;
        int right = center+1;
        while (left>=0){
            if(nums[left] == -1){
                left--;
            }else {
                ans[0]  = nums[left];
                break;
            }
        }
        while ( right <nums.length){
            if(nums[right] == -1){
                right++;
            }else {
                ans[2] = nums[right];
                break;
            }
        }
//        while (left>=0 &&right <nums.length){
//            if(nums[left]!=-1 &&nums[right]!=-1){
//                ans[0] = nums[left];
//                ans[2] = nums[right];
//                break;
//            }
//
//            if(nums[right] == -1){
//                right++;
//            }
//        }
        return ans[0]*ans[1]*ans[2];
    }

然后就可以通过回溯的方法求得结果,暴力回溯代码如下:

    public int maxCoins(int[] nums) {
        return process(nums,0);
    }
    private int process(int[] nums, int index) {
        if (nums.length == 0 || index == nums.length) {
            return 0;
        }
        int ans = 0;
        for(int i=0;i<nums.length;i++){
            if(nums[i] == -1){
                continue;
            }
            int tmp = nums[i];
            int curCoin = computeCurCoin(nums, i);
            nums[i] = -1;
            ans = Math.max(process(nums,index+1)+curCoin,ans);
            nums[i] = tmp;
        }
        return ans;
    }

记忆化递归

假设左边界为l右边界为r,戳破的位置为k,则当戳破以后,分为两个子问题:[l,k]和[k,j]。但是虽然还是求这两个范围的重复子问题,但是很明显这个不是独立的重叠子问题。因为还可能存在[k-2,k-1,k+1]这种情况。
因此,需要逆向思维,我们选择一个气球,假设他为最后一个,并点爆他。这个时候就可以把左边问题和右边问题独立化了!以[3,1,5]为例,先拿出来一个气球作为最后一个气球,然后点爆他。这样就能够将这个气球的左右两个子问题独立化。换个方式来说,我们选择1这个气球,然后优先吧3和5点爆之后,再最后点爆1这个气球。

=>定义状态转移方程。
dp[i][j]表示从i到j闭区间内能够获取最大硬币的数量。

dp[i][j] = dp[i][k-1]+dp[k+1][j] + nums[i-1]*nums[k]*nums[j+1]

这种方法仍然是O(n!),只是把重复子问题写在内存中,保证每种子问题只计算一遍。
代码如下:

    private int process_memo(int[] nums, int begin, int end, Integer[][] memo){
        if(memo[begin][end] !=null){
            return memo[begin][end];
        }
        int max = 0;
        for(int k=begin+1;k<end;k++){
            max = Math.max(max,
                    process_memo(nums,begin,k,memo) + process_memo(nums,k,end,memo) + nums[begin] * nums[k] * nums[end]);
        }
        return memo[begin][end] = max;
    }

     // 
    public int maxCoins(int[] nums) {
        int[] input = new int[nums.length+2];
        input[0] = 1;
        input[input.length-1] = 1;
        for(int i=0;i<nums.length;i++){
            input[i+1] = nums[i];
        }
        // 从0开始 到input.length-1结束。
        return process_memo(input,0,input.length-1,new Integer[input.length][input.length])
    }

动态规划

自底向上,所有明确计算粒度:从i到j,第一次长度是1,第二次长度为2,直到长度为n。

  • 第一层循环定义长度len from 1 to n
  • 第二层循环定义遍历的范围1 to n-len-1此时定义j的位置 j = i + len - 1
  • 第三层在当前情况下,选取k个气球,当做是最后一次点爆,一共有k种情况,(i<=k<=j)选取最大值。
    返回dp[1][n]
    代码如下:
    public int maxCoins(int[] nums) {
        int n = nums.length;
        int[][] rec = new int[n + 2][n + 2];
        int[] val = new int[n + 2];
        val[0] = val[n + 1] = 1;
        for (int i = 1; i <= n; i++) {
            val[i] = nums[i - 1];
        }
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 2; j <= n + 1; j++) {
                for (int k = i + 1; k < j; k++) {
                    int sum = val[i] * val[k] * val[j];
                    sum += rec[i][k] + rec[k][j];
                    rec[i][j] = Math.max(rec[i][j], sum);
                }
            }
        }
        return rec[0][n + 1];
    }
posted @   SsoZh  阅读(79)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
历史上的今天:
2019-11-13 Pycharm远程Linux显示图片
点击右上角即可分享
微信分享提示