LeetCode
- 题目地址:https://leetcode.com/problems/burst-balloons/#/description
题目描述&解题思路:给n个气球,每个气球有一个数字,用一个数组nums表示这些数字。戳破这个气球i可以得到nums[i]*nums[i-1]*nums[i+1]个硬币,可以假设nums[-1]和nums[n]=1。 求能获得的最大硬币个数。
nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
解决办法是动态规划,假设的dp[left][right]表示left到right区间,能获得的最大的硬币个数。本题的关键是找到最后一个戳的气球是哪个。要关注的点如下:
- dp[left][right]表示先戳破这个区间的气球,最多能得到多少个硬币,即在left到right区间内,最后一个戳破的气球k得到的硬币是nums[k]*nums[left-1]*nums[right+1]
- 计算dp[left][right]需要,任意a,b属于[left,right],dp[a][b],即从长度入手,先算长度小的再算长度大的
- 递推公式,找到气球k,是left到right区间内最后一个戳破的,那么dp[left][right] = max(dp[left][right],nums[left-1]*nums[k]*nums[right+1]+dp[left][k-1]+dp[k+1][right])
- 实现中的小技巧,可以把收尾的nums[-1]=nums[n]=1先插入数字,这样数组长度变成n+2,这时候不需要对边界做特殊处理
代码如下:
class Solution {
public:
int max(int a, int b) {
return a > b ? a : b;
}
int maxCoins(vector<int>& nums) {
//插入首尾两个1
int size = nums.size();
nums.insert(nums.begin(),1);
nums.push_back(1);
//记忆存储 加速dp
//插入两个1后,nums.size() = size + 2
vector<vector<int>> dp(size + 2, vector<int>(size + 2, 0));
//先算len = right-left+1小的,再算大的
for (int len = 0; len < size; len++) {
for (int left = 1; left <= size - len; left++) {
int right = left + len;
//left到right最后一个戳破的气球k
for (int k = left; k <= right; k++) {
dp[left][right] = max(dp[left][right], nums[left-1]*nums[k]*nums[right+1] + dp[left][k-1] + dp[k+1][right]);
}
}
}
return dp[1][size];
}
};