题目
戳气球问题 :输入一个包含非负数整数的数组nums代表一排气球, nums[i]代表第i个气球的分数。现在, 你要戳破所有的气球,请计算出最高的分数。
分数的计算规则比较特别,当戳破第i个气球时, 可以获得nums[left] * nums[right] * nums[i]的分数,其中nums[left]和nums[right]代表气球i的左右相邻气球的分数。
nums[left]不一定就是nums[i - 1],nums[right]不一定就是nums[i + 1]。比如戳破了nums[3], 现在nums[4]的左侧就和nums[2]相邻了。
思路分析
我们首先考虑的是‘动态规划’,此问题最小的子问题为戳破最后一个气球,但这个最后一个戳破的气球可以是范围内的其中一个,所以我们借助循环,通过左右区域的最高分数就可以求出以当前气球为最后一个戳破最高分数。
我们可以建立一个二维数组来记录当前区间的最高分数。
在求当前最高分时,我们要先求出左右区域的最高分数,所以大概的遍历顺序为下图
从下往上从左往右遍历。由于被戳破的最后一个气球的位置不固定,所以我们要求出的左右区域的最高分数也不止两个。通过循环我们就可以求出所有不同位置的左右区域的最高分数。不同最后一个被戳破的气球的位置对应的左右区域的最高分数的分布为下图。
代码展示
static public int maxCoins(int[] nums) {
int n = nums.length;
//为了方便,我们就让下标从一开始,所以边界为0到n + 1;
int[] points = new int[n + 2];//都初始化为0
for(int i = 1; i <= n; i++) {
points[i] = nums[i - 1];
}
points[0] = points[n + 1] = 1;//初始化为一从而保证了在后续的循环中防止边界的分数影响最终结果
int[][] dp = new int[n + 2][n + 2];
for(int i = n; i >= 0; i--)
for(int j = i + 1; j < n + 2; j++) {
for(int k = i + 1; k < j; k++) {
//(i,j) i到j为开区间
dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[k][j] + points[i] * points[j] * points[k]);
/*因为i~k和k~j之间的气球已经全破了,所以k的左右为i,j */
}
}
return dp[0][n + 1];
}