之前看到这个问题,没有头绪,参考了http://www.tuicool.com/articles/NZBNZ3Z的解法,里面循环的问题稍微理清了一下。
废话不说,先上题。
有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
这个DP问题可以看作是,对于某个特定的气球,其得分等于 吹爆两边的气球,再吹中间的那个气球。
我们定义一个dp[i][j], 表示吹爆编号从 i 到 j 的气球所得的最大分数。
dp[i][j] = Max{dp[i][k-1] + dp[k+1][j] + nums[i-1] * nums[k] * nums[j+1]}, 式中i+1 <= k <= j-1,即至少要求有3个气球存在(不考虑辅助的nums[-1] 和 nums[n);
对于j = i + 1来说,dp[i][j] = max{先吹爆i 再吹爆j, 先吹爆j 再吹爆i }
public int booloon(int[] nums){
int n = nums.length;
int[] arr = new int[nums.length + 2];
arr[0] = 1;
arr[arr.length - 1] = 1;
for(int i = 0; i < nums.length; i++)
arr[i+1] = nums[i];
int[][] dp = new int[n+2][n+2];
// 边界条件:只吹爆编号为i的气球的得分
for(int i = 1; i <= n; i++)
dp[i][i] = arr[i-1] * arr[i] * arr[i+1];
for(int i = n; i >= 1; i--)
for(int j = i+1; j <= n; j++){
// 只有2个的情况
dp[i][j] = Math.max(dp[i][j-1] + arr[i-1] * arr[j] * arr[j+1],
dp[i+1][j] + arr[i-1] * arr[i] * arr[j+1]
);
// 至少3个的情况
for(int k = i+1; k <= j-1; k++)
dp[i][j] = Math.max(dp[i][j], dp[i][k-1] + dp[k+1][j] + arr[k] * arr[i-1] * arr[j+1]);
}
return dp[1][n];
}