http://blog.youkuaiyun.com/zlambert/article/details/65934717
题目描述
有n个气球,编号为0到n-1,每个气球都有一个分数,存在nums数组中。每次吹气球i可以得到的分数为 nums[left] * nums[i] * nums[right],left和right分别表示i气球相邻的两个气球。当i气球被吹爆后,其左右两气球即为相邻。要求吹爆所有气球,得到最多的分数。
注释:
(1) 你可以假设nums[-1] = nums[n] = 1
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
Example:
给出[3, 1, 5, 8]
返回167
class Solution {
int max = Integer.MIN_VALUE;
//枚举所有case
public int solve(int[] nums){
if(nums==null) return max;
int n = nums.length;
List<Integer> list = new ArrayList<Integer>();
for(int i=0; i<n; i++){
list.add(nums);
}
int res=0;
process(list, res);
return max;
}
public void process(List list, int res){
if(list.size()==0)
max = max(max, res);
for(int i=0; i<list.size(); i++){
//blow ballon i
int curMul = (i-1<0?1:list.get(i-1))*list.get(i)*(i+1>=n?1:list.get(i+1));
res += curMul;
int cur = list.remove(i);
process(list, res);
res -= curMul;
list.add(i, cur);
}
}
//递归-记忆
public int solve_memo(int[] nums){
int n = nums.length;
int[] arrs = new int[n+2];
for(int i=0; i<n; i++){
arrs[i+1] = nums[i];
}
arrs[0]=1;
arrs[n+1]=1;
//dp[i][j]表示吹爆i~j间的气球(包含i、j)能得到的最大收益
int[][] dp = new dp[n+2][n+2];
process_memo(arrs, dp, 1, n);
return dp[1][n];
}
public int process_memo(int[] nums, int[][] dp, int s, int e){
if(dp[s][e]>0)
return dp[s][e];
int maxCur=0;
for(int k=s, k<=e; k++){
int left = process(dp, s, k-1);
int right = process(dp, s+1, e);
int mid = nums[s-1]*nums[k]*nums[e+1];
maxCur = Math.max(maxCur, left+mid+right);
}
dp[s][e] = maxCur;
return maxCur;
}
//动态规划
public int solve_dp(int[] nums){
if(nums==null) return max;
int n = nums.length;
int[] arrs = new int[n+2];
for(int i=0; i<n; i++){
arrs[i+1] = nums[i];
}
arrs[0]=1;
arrs[n+1]=1;
//dp[i][j]表示吹爆i~j间的气球(包含i、j)能得到的最大收益
int[][] dp = new int[n+1][n+1];
//状态转移
for(int len=1; len<=n; len++){
for(int s=1; s<=n; s++){
int curMax = 0;
int e=s+len-1;
for(int k=s; k<=e; k++){
curMax = Math.max(curMax, dp[s][k-1]+dp[k+1][e]+arrs[s-1]*arrs[k]*arrs[e+1]);
}
dp[i][j] = curMax;
}
}
return dp[1][n];
}
}