题意简述:给出一排气球(用0到n-1代表),每个气球都有一个分数,打爆一个气球可以获得该气球以及它左右气球的三个分数的乘积,并且打爆之后,左右气球变成相邻。求出打爆所有气球所能获得的分数最大值。
输入:一个vector,表示每个气球的分数
输出:分数的最大值
示例:对于4个气球,分数分别为[3,1,5,8],按气球1、2、0、3的顺序打爆,可以获得最大分数
3*1*5+3*5*8+3*8+8=167。
题解:
采用动态规划求解。
首先对于最左或者最右的气球而言,打爆它只会进行两个分数的乘积。如果设定人为的边界,即在左右两边设置一个虚拟的(不能被打爆)、分数为1的气球,那么处理真实的最左最右气球就会跟中间的气球一样进行三个分数的乘积。
考虑求解左右边界分别为i和j(气球i和气球j不打爆,仅用于计算分数)的区间(i,j)获得分数的最大值(记为 dp(i,j) )。这里采用的dp策略是考虑区间内最后被打爆的气球k,因为打爆其他气球的时候气球k肯定存在,所以气球k实际上将区间(i,j)分为(i,k)和(k,j)两部分,于是打爆区间(i,j)的所有气球可以分为三个步骤完成:
- 打爆区间(i,k)的所有气球;
- 打爆区间(k,j)的所有气球;
- 最后打爆气球k,由于k是区间最后一个气球,因此气球k与气球i和气球j相邻。
由此得到的状态转移方程是
dp(i,j)=max(dp(i,k)+nums[i]∗nums[k]∗nums[j]+dp(k,j)), i<k<j
边界情况是 dp(i,i)=0,dp(i,i+1)=0
根据方程,求解区间长为x的情况需要先求解区间长为1、2、……、x-1的情况,因此中间结果不能用一维数组储存而只能用二维数组;与此同时可以采用递推的方法进行求解。
算法的时间复杂度是 O(n3) ,空间复杂度是 O(n2) 。
class Solution {
public:
int maxCoins(vector<int>& nums) {
// add the border
nums.push_back(1);
nums.insert(nums.begin(),1);
int n = nums.size();
// initialization
vector<vector<int>> dp;
for(int i = 0;i < n; i++) {
vector<int> temp(n, 0);
dp.push_back(temp);
}
// dp
for(int len = 2;len < n;len++) {
for(int i = 0;i < n - len;i++) {
for(int j = i+1;j < i+len;j++) {
int temp = dp[i][j] + nums[i]*nums[j]*nums[i+len] + dp[j][i+len];
dp[i][i+len] = max(dp[i][i+len], temp);
}
}
}
return dp[0][n-1];
}
};