zoukankan      html  css  js  c++  java
  • 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];
        }
    
  • 相关阅读:
    Java基础教程
    一个RDBMS左连接SQL执行计划解析
    hive时间日期函数及典型场景应用
    ETL数据采集方法
    数据仓库保存历史数据方法之拉链表
    NAS服务器局域网内IPad、手机、电视盒子等联网播放
    转:主流数据恢复软件——EasyRecovery/Ashampoo Undeleter/Wise Data Recovery/Recuva/Undelete 360
    [转]office2010一直卡在“正在受保护的视图中打开”
    [转]PROE传动链条的装配教程
    linux下svn定时更新项目
  • 原文地址:https://www.cnblogs.com/SsoZhNO-1/p/13969974.html
Copyright © 2011-2022 走看看