问题
例子
思路
回溯时间太久【第一次有n个选择,第二次有n-1个选择,时间为O(n!)】
动态规划【有最字】+分治
区间[i, j] 戳破的气球为k。
k戳破后,区间变成了[i, k) 和(k, j],即[i, i+1, … k-1, k+1, …j],那么k+1啥时候戳破对[i, k)的结果有影响,如果先戳破k-1,再戳破k+1,k-1的右边是k+1,计算时[k-2]*[k-1]*[k+1],如果先戳破,(k, j]中的k+1,再戳破[i, k)中的k-1,此时k-1右边的变成了k+2,计算时[k-2]*[k-1]*[k+2]
,即左右两边区间的计算产生了依赖。
如何不产生依赖呢?气球k,放在最后戳爆它。
k为区间[i, j]间最后一个戳爆的气球,它把[i, j]分为了[i, k-1]和[k+1, j]两个区间,可分别求出
-
方法1
状 态 转 移 方 程 d p [ i ] [ j ] 为 [ i , j ] 区 间 的 最 大 值 d p [ i ] [ j ] = m a x ( d p [ i ] [ k − 1 ] + d p [ k + 1 ] [ j ] + a r r [ i − 1 ] ∗ a r r [ k ] ∗ a r r [ j + 1 ] ) i < = k < = j 状态转移方程~~ dp[i][j]为[i,j]区间的最大值\\ dp[i][j]=max(dp[i][k-1]+dp[k+1][j]+arr[i-1]*arr[k]*arr[j+1]) ~~i<=k<=j 状态转移方程 dp[i][j]为[i,j]区间的最大值dp[i][j]=max(dp[i][k−1]+dp[k+1][j]+arr[i−1]∗arr[k]∗arr[j+1]) i<=k<=j -
方法2
$$$$
代码
//方法1 自顶向下
class Solution {
public int maxCoins(int[] nums) {
//创建虚拟边界
int[] arr = new int[nums.length+2];
System.arraycopy(nums,0,arr,1,nums.length);
arr[0]=1;
arr[arr.length-1]=1;
int[][] dp = new int[arr.length][arr.length];
return get(arr,1,arr.length-2,dp);
}
public int get(int[] arr,int i,int j, int[][] dp) {
if(i>j) return 0;
if(dp[i][j]>0) return dp[i][j];
int max = 0;
for(int k=i; k<=j; k++) {
max = Math.max(max, arr[i-1]*arr[k]*arr[j+1]+get(arr, i,k-1,dp)+get(arr,k+1,j,dp));
}
dp[i][j]=max;
return max;
}
}
//方法2 自下而上
class Solution {
public int maxCoins(int[] nums) {
int n = nums.length;
//创建虚拟边界
int[] arr = new int[n+2];
System.arraycopy(nums,0,arr,1,n);
arr[0]=1;
arr[n+1]=1;
int[][] dp = new int[n+2][n+2];
for(int len = 1; len<=n; len++) {
for(int i=1; i+len-1<=n; i++) {
int j = i+len-1;
for(int k=i; k<=j; k++) {
dp[i][j]=Math.max(dp[i][j], arr[i-1]*arr[k]*arr[j+1]+dp[i][k-1]+dp[k+1][j]);
}
}
}
return dp[1][n];
}