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)
  • 相关阅读:
    Flutter form 的表单 input
    FloatingActionButton 实现类似 闲鱼 App 底部导航凸起按钮
    Flutter 中的常见的按钮组件 以及自 定义按钮组件
    Drawer 侧边栏、以及侧边栏内 容布局
    AppBar 自定义顶部导航按钮 图标、颜色 以及 TabBar 定义顶部 Tab 切换 通过TabController 定义TabBar
    清空路由 路由替换 返回到根路由
    应对ubuntu linux图形界面卡住的方法
    [转] 一块赚零花钱
    [转]在树莓派上搭建LAMP服务
    ssh保持连接
  • 原文地址:https://www.cnblogs.com/liujiaa/p/7985895.html
Copyright © 2011-2022 走看看