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)