阿里测试算法题,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)

posted on 2017-12-05 11:03  刘子哥  阅读(225)  评论(0编辑  收藏  举报

导航