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)