312 Burst Balloons
题目
题解
首先对问题进行下列分析
-
问题可以被划分为子问题吗?
打气球时,剩下的气球的maximum coins与已经被打掉的气球无关,因此本问题可以逐步转化为更小的子问题。
-
较小规模的子问题可以用来辅助求解较大规模的子问题吗?
假设我们已经知道规模在k以内的子问题的解,现在需要求解规模为k+1的问题的解。因为我们无法预先知道打气球的中间过程中各个气球的邻居,所以我们只有两种方法:①选出一个气球作为第一个打爆;②选出一个气球作为最后一个打爆。在气球\(x\)被打爆后,我们可以算出打爆它所得到的coins,然后加上\(x\)左边和\(x\)右边两部分的子问题之解。
但是,第一种做法行不通,因为在第一个气球\(x\)被打爆后,\(x\)的左边部分会与右边部分相邻,从而产生出新的子问题,因此我们只能采用第二种方法,即把气球\(x\)放在最后打爆。
记\(dp[left][right]\)为某一个问题的解,即打爆区间(left,right)内所有气球的maximum coins,\(nums[i]\)表示下标为\(i\)的气球上的数字,那么我们有 \(dp[left][right] = max(dp[left][right] , \\ dp[left][i] + nums[left] * nums[i] * nums[right] + dp[i][right]) \\ \text{for all i such that } left \lt i \lt right\)
因为气球\(i\)是最后才打爆的,所以打爆它得到的coins是\(nums[i]\)乘以两个边界上的值。因为气球\(i\)是在左右两边的气球都打爆后才被打爆的,所以可以直接利用左右两部分的解,即\(dp[left][i]\)和\(dp[i][right]\)进行计算。
本问题属于动态规划问题,求解这种问题的关键往往在于
- 如何把问题划分成子问题
- 如何利用较小规模的子问题求解较大规模的子问题
代码
class Solution
{
public:
int maxCoins(vector<int> &nums)
{
int numberSize = nums.size();
int extendedSize = numberSize + 2;
int extendedNums[extendedSize];
extendedNums[0] = 1;
for (int i = 0; i < numberSize; i++)
{
extendedNums[i + 1] = nums[i];
}
extendedNums[extendedSize - 1] = 1;
int dynamicProgramming[extendedSize][extendedSize];
for (int i = 0; i < extendedSize; i++)
{
for (int j = 0; j < extendedSize; j++)
{
dynamicProgramming[i][j] = 0;
}
}
for (int k = 2; k < extendedSize; k++)
{
for (int left = 0; left < extendedSize - k; left++)
{
int right = left + k;
for (int i = left + 1; i < right; i++)
{
dynamicProgramming[left][right] = max(dynamicProgramming[left][right],
dynamicProgramming[left][i] +
extendedNums[left] * extendedNums[i] * extendedNums[right] +
dynamicProgramming[i][right]);
}
}
}
return dynamicProgramming[0][extendedSize - 1];
}
};