zoukankan      html  css  js  c++  java
  • 阿里测试算法题,Burst Balloons 问题的变形

    public static void main(String[] args) {
             List<Integer>list=Arrays.asList(4,4,3,1);
             // 循环的次数 C(N+2),3
             System.out.println(count(list));
            
         }
         
         public static int count(List<Integer> list){
             int n = list.size();
             List<Integer>tmplist=new ArrayList<Integer>();
             int sum=0;
             for(int i=0;i<n;i++){
                 // 以0为分界线,分成不同的子集,再求这些子集合的和,再全部加起来
                 if(list.get(i)!=0)
                     tmplist.add(list.get(i));
                 else{
                     int []a=new int[tmplist.size()];
                     for(int j=0;j<tmplist.size();j++){
                         a[j]=tmplist.get(j);
                     }
                     sum+=maxCoins(a);
                     tmplist.clear();
                 }
                 // 求最后那个子集合的和
                 if(i==n-1){
                     int []a=new int[tmplist.size()];
                     for(int j=0;j<tmplist.size();j++){
                         a[j]=tmplist.get(j);
                     }
                     sum+=maxCoins(a);
                     tmplist.clear();
                 }
             }
            // System.out.println(sum);
             return sum;
         }
         
         public static int maxCoins(int[] iNums) {
             int[] nums = new int[iNums.length + 2];
             int n = 1;
             // 在集合的头尾加上两个编号为1的气球
             for (int x : iNums) if (x > 0) nums[n++] = x;
             nums[0] = nums[n++] = 1;
             int[][] memo = new int[n][n];
             return burst(memo, nums, 0, n - 1);
         }
         
         static int count = 0;
     
          /**
              * 以 4,4,3,1为例子 
            * 定义:burst(left,right),是计算left~right之间最的大得分,计算的方法是循环 i=1~4 , 递归求right=burst(left,i)
            * left=burst(i,right) 以及temp = nums[left]*nums[i]*nums[right] ,在与上次循环的结果ans比较,
            * 取max(ans,left+temp+right) , 注意这是开区间! (3,5)的最大得分用闭区间表示就是[4]这一个气球的得分!
            * 
            * @param memo :二维数组,存储某范围内的最大得分 例如 (3,5)的值 表示 3~5最大的得分,在递归中都是(left,right)进行计算的
            * @param nums
            * @param left :当前递归时候的最left值
            * @param right:当前递归时候的最right值
            * @return
            *
            * 
            * (1)在第一层递归第一次计算burstLeft的时候,由于 i=left+1,所以burstLeft根本就不存在,可见方法定义,比如3~4的(3,4)不存在任何气球
            * 所以会进入burstRight的递归,接下来每层递归的第一层循环都是这样,即,i=left+1,burstLeft不存在,就会一直往深处递归到right-2
            * 到此时,经过的流程为
            * 1 left:0 i:1 right:5  TEMP:4
              2 left:1 i:2 right:5  TEMP:16
              3 left:2 i:3 right:5  TEMP:12
              4 left:3 i:4 right:5  TEMP:3
            * (2)此时,burstLeft=(left,i):(3,4) ; burstRight=(i,right):(4,5)都不存在
            * ans=nums[3]*nums[4]*nums[5]。把值存入memo[3][5](只有一个气球4,所以肯定是最大值),返回到left等于2的递归。
            * 
            * (3)返回的地点是burstRight=(i,right),其中i=3,right=5; 运行到此刻表示的含义是,当left=2,i=3,right=5
            * TEMP=nums[2]*nums[3]*nums[5] =12 burstLeft=(2,3)=0  burstRight=(3,5)=3 ,算到了一种burst(2,5)
            * 的可能 nums[2]*nums[3]*nums[5]+burstLeft+burstRight=15。
            * 
            * (4)然再 i+1 = 4 < right:5 继续循环,算出TEMP=nums[2]*nums[4]*nums[5] = 4 , 
            * burstLeft=burst(2,4)=12 burstRight=(4,5)=0 所以算的burst(2,5)另外一种可能:
            * nums[2]*nums[4]*nums[5]+burstLeft+burstRight=16。
            * 
            * (5)然后根据max(ans,...)--->15<16, 拿算得burst(2,5)=16。
            * 
            * 
            * 综上所述每次求temp=nums[left]*nums[i]*nums[right]的时候,其实相当于求burst(left,right)中最中最后一步的
            * 计算。比如上例子求先算burst(2,5)中的 nums[2]*nums[4]*nums[5],再算burst(2,4)和burst(4,5),但实际情况是
            * 先完成burst(2,4)(此时的burst(4,5)不存在,没什么用)才可以进行 nums[2]*nums[4]*nums[5]的计算。
            * 由此可以总结出。该算法的循环的i是确定最后射击的那个气球,然后再不断递归求得 (left,i) 和 (i,right)
            * 
            */
         public static int burst(int[][] memo, int[] nums, int left, int right) {
             if (left + 1 == right) return 0;
             if (memo[left][right] > 0) {
                // System.out.println("left: "+left+" right:"+right+" memo[left][right]:"+memo[left][right]);
                 return memo[left][right];
             }
             int ans = 0;
             for (int i = left + 1; i < right; ++i){
                 int temp = nums[left] * nums[i] * nums[right] ;
                System.out.println(++count+" left:"+left + " i:" + i + " right:"+right +"  TEMP:"+temp);
                 int burstLeft =  burst(memo, nums, left, i);
                 int burstRight = burst(memo, nums, i, right);
                 ans = Math.max(ans , burstLeft+temp+burstRight);
                
             }
             memo[left][right] = ans;
              System.out.println("left: "+left+" right:"+right+" memo[left][right]:"+memo[left][right]);
             return ans;
         }
    以4,4,3,1为例子
     定义:burst(left,right),是计算left~right之间最的大得分,计算的方法是循环 i=1~4 , 递归求right=burst(left,i)
          * left=burst(i,right) 以及temp = nums[left]*nums[i]*nums[right] ,在与上次循环的结果ans比较,
          * 取max(ans,left+temp+right) , 注意这是开区间! (3,5)的最大得分用闭区间表示就是[4]这一个气球的得分!
          * 
          * @param memo :二维数组,存储某范围内的最大得分 例如 (3,5)的值 表示 3~5最大的得分,在递归中都是(left,right)进行计算的
          * @param nums
          * @param left :当前递归时候的最left值
          * @param right:当前递归时候的最right值
          * @return
          * 以4,4,3,1为例子
          * 
          * (1)在第一层递归第一次计算burstLeft的时候,由于 i=left+1,所以burstLeft根本就不存在,可见方法定义,比如3~4的(3,4)不存在任何气球
          * 所以会进入burstRight的递归,接下来每层递归的第一层循环都是这样,即,i=left+1,burstLeft不存在,就会一直往深处递归到right-2
          * 到此时,经过的流程为
          * 1 left:0 i:1 right:5  TEMP:4
            2 left:1 i:2 right:5  TEMP:16
            3 left:2 i:3 right:5  TEMP:12
            4 left:3 i:4 right:5  TEMP:3
          * (2)此时,burstLeft=(left,i):(3,4) ; burstRight=(i,right):(4,5)都不存在,
          * ans=nums[3]*nums[4]*nums[5]。把值存入memo[3][5](只有一个气球4,所以肯定是最大值),返回到left等于2的递归。
          * 
          * (3)返回的地点是burstRight=(i,right),其中i=3,right=5; 运行到此刻表示的含义是,当left=2,i=3,right=5
          * TEMP=nums[2]*nums[3]*nums[5] =12 burstLeft=(2,3)=0  burstRight=(3,5)=3 ,算到了一种burst(2,5)
          * 的可能 nums[2]*nums[3]*nums[5]+burstLeft+burstRight=15。
          * 
          * (4)然再 i+1 = 4 < right:5 继续循环,算出TEMP=nums[2]*nums[4]*nums[5] = 4 , 
          * burstLeft=burst(2,4)=12 burstRight=(4,5)=0 所以算的burst(2,5)另外一种可能:
          * nums[2]*nums[4]*nums[5]+burstLeft+burstRight=16。
          * 
          * (5)然后根据max(ans,...)--->15<16, 拿算得burst(2,5)=16。
          * 
          * 
          * 综上所述每次求temp=nums[left]*nums[i]*nums[right]的时候,其实相当于先求burst(left,right)中最中最后一步的
          * 计算。比如上例子求先算burst(2,5)中的 nums[2]*nums[4]*nums[5],再算burst(2,4)和burst(4,5),但实际情况是
          * 先完成burst(2,4)(此时的burst(4,5)不存在,没什么用)才可以进行 nums[2]*nums[4]*nums[5]的计算。
          * 由此可以总结出。该算法的流程是,循环i确定最后射击的那个气球,然后再不断递归求得 (left,i) 和 (i,right)
  • 相关阅读:
    jquery键盘事件全记录
    ASP.NET Session的七点认识
    11个强大的Visual Studio调试小技巧
    javascript 执行顺序详解
    编程笔记:JavaScript 中的类型检查
    js中实现文件上传下载的三种解决方案(推荐)
    前端中实现文件上传下载的三种解决方案(推荐)
    网页中实现文件上传下载的三种解决方案(推荐)
    Web中实现文件上传下载的三种解决方案(推荐)
    B/S中实现文件上传下载的三种解决方案(推荐)
  • 原文地址:https://www.cnblogs.com/liujiaa/p/7985895.html
Copyright © 2011-2022 走看看