思路
这题如果正向思考很难解决:先戳破第 i 个气球,会发现后面的计算过于复杂。但是如果逆向思考:最后戳破第 i 个气球,那么区间范围 [i, j] 中的所有除它以外的气球都破了,就很方便计算出结果。状态转移方程是:
resLeft + resRight + left * nums[k] * right
自顶向下带有记忆的分治
int[][] dp = null;
public int maxCoins2(int[] nums) {
if (nums.length == 0)
return 0;
dp = new int[nums.length][nums.length];
return f(0, nums.length - 1, nums);
}
int f(int i, int j, int[] nums) {
if (dp[i][j] != 0)
return dp[i][j];
if (i == j) {
dp[i][j] = nums[i];
if (i > 0)
dp[i][j] *= nums[i - 1];
if (j < nums.length - 1)
dp[i][j] *= nums[j + 1];
return dp[i][j];
}
int left = 1, right = 1;
if (i > 0)
left = nums[i - 1];
if (j < nums.length - 1)
right = nums[j + 1];
for (int k = i; k <= j; ++k) {
int resLeft = 0, resRight = 0;
if (i != k)
resLeft = f(i, k - 1, nums);
if (k != j)
resRight = f(k + 1, j, nums);
dp[i][j] = Math.max(dp[i][j], resLeft + resRight + left * nums[k] * right);
}
return dp[i][j];
}
自下向上的动态规划
public int maxCoins(int[] nums) {
if (nums.length == 0)
return 0;
int[][] d = new int[nums.length][nums.length];
for (int i = nums.length - 1; i >= 0; --i) {
for (int j = i; j < nums.length; ++j) {
int left = 1, right = 1, leftRes = 0, rightRes = 0;
if (i > 0)
left = nums[i - 1];
if (j < nums.length - 1)
right = nums[j + 1];
if (i == j)
d[i][j] = left * right * nums[i];
else
for (int k = i; k <= j; ++k) {
leftRes = rightRes = 0;
if (k > i)
leftRes = d[i][k - 1];
if (k < j)
rightRes = d[k + 1][j];
d[i][j] = Math.max(d[i][j], left * right * nums[k] + leftRes + rightRes);
}
}
}
return d[0][nums.length - 1];
}