题目描述
有 n
个气球,编号为0
到 n - 1
,每个气球上都标有一个数字,这些数字存在数组 nums
中。
现在要求你戳破所有的气球。戳破第 i
个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1]
枚硬币。 这里的 i - 1
和 i + 1
代表和 i
相邻的两个气球的序号。如果 i - 1
或 i + 1
超出了数组的边界,那么就当它是一个数字为 1
的气球。
求所能获得硬币的最大数量。
问题分析
考虑戳破编号从 i 到 j 的气球,所能获取硬币的最大值。我们假设,最后戳破的气球的编号为 k ,则所能获取硬币的最大值是从 i 到 k -1 的最大值加上从 k+1 到 j 的最大值,最后加上戳破 k 所能获取的硬币数。通过遍历 k 的每个取值,我们最终就能获取戳破编号从 i 到 j 的气球,所能获取硬币的最大值。
上述的分析是自上而下的。可以发现,问题规模较大的解是通过问题规模较小的解组合而成的。因此,我们可以自下而上,采用区间 DP 的策略,从区间长度为 2 的规模,一直遍历到区间长度为 n 的规模,最终得到最优解。
为了简化状态计算过程,我们可以在数组的首尾分别添加一个元素 1,防止计算过程中下标越界。
算法描述
状态定义:dp[i][j]
表示戳破编号从 i 到 j 的气球,所能获取硬币的最大值。
状态计算:dp[i][j] = max(dp[i][j], dp[i][k-1] + dp[k+1][j] + a[i-1]*a[k]*a[j+1])
边界情况:
dp[i][i] = a[i-1]*a[i]*a[i+1]
- 若
i > j
:dp[i][j] = 0
复杂度法分析
时间复杂度:
O
(
n
3
)
O(n^3)
O(n3)
空间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
程序代码
class Solution {
public:
int maxCoins(vector<int>& nums) {
int n = nums.size();
vector<int> a(n + 2);
for(int i = 1; i <= n; i++) {
a[i] = nums[i-1];
}
a[0] = a[n+1] = 1;
vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
for(int i = 1; i <= n; i++) {
dp[i][i] = a[i-1]*a[i]*a[i+1];
}
for(int len = 2; len <= n; len++) {
for(int i = 1; i <= n - len + 1; i++) {
int j = i + len - 1;
for(int k = i; k <= j; k++) {
dp[i][j] = max(dp[i][j], dp[i][k-1] + dp[k+1][j] + a[i-1]*a[k]*a[j+1]);
}
}
}
return dp[1][n];
}
};