问题描述:
有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
现在要求你戳破所有的气球。每当你戳破一个气球 i 时,你可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。
求所能获得硬币的最大数量。
示例:
输入: [3,1,5,8]
输出: 167
解释: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 167
若气球编号为0 到 n-1,当最后戳破气球k时,此时数组里仅剩第k个气球,因此得到的硬币数应该为nums[-1]*nums[k]*nums[n]。设从i到j编号的气球得到的最大硬币数为dp[i][j].
那么得到的总硬币数应该是dp[i][k]+dp[k][j]+nums[-1]*nums[k]*nums[n]。若要找到最大的硬币数,则需要找到最大的分割点k。
即可得到转移方程:
dp[i][j] = max(dp[i][k]+dp[k][j]+nums[-1]*nums[k]*nums[n])
由于计算dp[i][j]需要(i,j)左侧和下方的结果,因此需要斜向遍历。
具体实现如下:
class Solution {
public:
int maxCoins(vector<int>& nums) {
nums.insert(nums.begin(), 1);
nums.push_back(1);
int n = nums.size();
vector<int> t(n, 0);
vector<vector<int>> dp(n, t);
//dp[i][j] = max(dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j])
for(int len=2;len<n;len++)
{
for(int i=0;i+len<n;i++)
{
int j = i + len;
for(int k=i+1;k<j;k++)
{
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]);
}
}
}
return dp[0][n-1];
}
};
与此类似的矩阵乘法问题。
问题描述:
给定n个矩阵:A1,A2,...,An,其中Ai与Ai+1是可乘的,i=1,2...,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。
同样需要找到合适的分割点k,来得到最小值。
其转移方程为:
dp[i][j] = min(dp[i][k]+dp[k][j]+p[i-1]p[k]p[j]
其中,p[i-1]是第i个矩阵的行数,p[j]是第j个矩阵的列数。