每天一道LeetCode-----买卖商品问题,计算最大利润,分别有一次交易,两次交易,多次交易的情况
2018-01-10 14:37
681 查看
Best Time to Buy and Sell Stock
原题链接Best Time to Buy and Sell Stock给定一个价格序列prices,其中prices[i]代表第i天商品的价格,商家需要在某一天买入,然后在之后的某一天出售,计算可以获得的最大利润
本质就是计算prices[i]−prices[j]的最大值,要求i>j
假设已经直到最后结果的最大值位置i(prices[i]),只需要减去最小值即可,那么就需要直到最小值是多少,本题中当从左到右遍历时,完全可以随着遍历进度的增加记录最小值
代码如下
class Solution { public: int maxProfit(vector<int>& prices) { if(prices.empty()) return 0; /* 记录目前位置找到的最小值 */ int minPrices = prices[0]; /* 记录最大差值 */ int maxProfit = 0; for(int i = 1; i < prices.size(); ++i) { /* 以prices[i]作为最大值,尝试计算差值 */ maxProfit = std::max(maxProfit, prices[i] - minPrices); /* 尝试更新最小值 */ minPrices = std::min(minPrices, prices[i]); } return maxProfit; } };
当然也可以遍历两次求解,第一次对于每个位置的值,计算它的右侧比它大的最大值,第二次对于每个位置,计算右侧的最大值与自己的差,更新最大差值,求出结果
代码如下
class Solution { public: int maxProfit(vector<int>& prices) { int n = prices.size(); vector<int> dp(n + 1, 0); /* 计算prices[i]右侧的最大值,包括自己 */ for(int i = n - 1; i >= 0; --i) dp[i] = std::max(dp[i + 1], prices[i]); int maxProfit = 0; /* 右侧最大值 - 自身 = 最大差值 */ for(int i = 0; i < n; ++i) maxProfit = std::max(maxProfit, dp[i] - prices[i]); return maxProfit; } };
Best Time to Buy and Sell Stock II
原题链接Best Time to Buy and Sell Stock II要求和上面一样,不过这次可以进行多次交易,即买,卖,买,卖,买,卖…,其中买卖不能在同一天进行
这样就不能像上面那样只求一次了,其实仔细想一下,序列无非三种排列
递增,假设A<B<C<D
递减,假设A>B>C>D
无序,假设A<B>C<D
对于递增序列,最大的差值就是D−A,因为(D−A)=(D−C)+(C−B)+(B−A)>(D−C)+(B−A)
对于递减序列,为0
对于无序序列,总可以找到若干个递增序列,就上面的例子而言最大差值为(B−A)+(D−C),而实际上也可以写成(D−C)+max(C−B,0)+(B−A),和递增序列的形式是一样的
所以只要依次计算prices[i]−prices[i−1]即可
代码如下
class Solution { public: int maxProfit(vector<int>& prices) { int maxProfit = 0; for(int i = 1; i < prices.size(); ++i) maxProfit += std::max(prices[i] - prices[i - 1], 0); return maxProfit; } };
Best Time to Buy and Sell Stock III
原题链接Best Time to Buy and Sell Stock III同样是买卖问题,本题要求最多可以完成两次交易,计算最大利润
利用动态规划,对动态规划数组states[i][j]有如下规定
states[][0]代表0次买,0次卖的利润
states[][1]代表一次买,0次卖的利润(显然是负数)
states[][2]代表一次买,一次卖的利润
states[][3]代表两次买,一次卖的利润
states[][4]代表两次买,两次卖的利润
而states,规定它是二维数组,states[0]代表当前的利润,states[1]代表执行完本次操作后的利润,那么有
states[1][1] = std::max(states[0][1], states[0][0] - prices[i]),表示买入商品prices[i],那么利润就是0次买0次卖的利润减商品价格,当然需要和1次买0次卖比较,选择较大的那个
states[1][2] = std::max(states[0][2], states[0][1] + prices[i]),表示卖出商品prices[i],那么利润就是1次买0次卖的利润加商品价格,当然需要和1次买1次卖比较,选择较大的那个
…同理
初始状态,另1次买0次卖的利润为INT_MIN,两次买一次卖的利润为INT_INT,是为了让max选择后者,因为初始状态没有买入任何商品
代码如下
class Solution { public: int maxProfit(vector<int>& prices) { vector<vector<int>> states(2, vector<int>(2 * 2 + 1, 0)); states[0][0] = 0; for(int i = 1; i < 2 * 2 + 1; i += 2) { states[0][i] = INT_MIN; states[0][i + 1] = 0; } int cur = 0, next = 1; for(int i = 0; i < prices.size(); ++i) { states[next][1] = std::max(states[cur][1], states[cur][0] - prices[i]); states[next][2] = std::max(states[cur][2], states[cur][1] + prices[i]); states[next][3] = std::max(states[cur][3], states[cur][2] - prices[i]); states[next][4] = std::max(states[cur][4], states[cur][3] + prices[i]); /* next变为当前 */ std::swap(next, cur); } return states[cur][2 * 2]; } };
Best Time to Buy and Sell Stock IV
原题链接Best Time to Buy and Sell Stock IV和上面一样,要求最多可以进行k次交易
只需要将上面的两次交易换成k次即可
class Solution { public: int maxProfit(int k, vector<int>& prices) { vector<vector<int>> states(2, vector<int>(2 * k + 1, 0)); states[0][0] = 0; for(int i = 1; i < 2 * k + 1; i += 2) { states[0][i] = INT_MIN; states[0][i + 1] = 0; } int cur = 0, next = 1; for(int i = 1; i < prices.size(); ++i) { /* 把两次变为k次,就需要放在循环里了 */ for(int j = 1; j < 2 * k + 1; j += 2) { states[next][j] = std::max(states[cur][j], states[cur][j - 1] - prices[i]); states[next][j + 1] = std::max(states[cur][j + 1], states[cur][j] + prices[i]); } std::swap(next, cur); } return states[cur][2 * k]; } };
不过这样可能内存溢出,因为如果k很大,申请的内存很容易溢出,解决方法是当k很大时采用其他方法。什么时候k算大呢,就是k次交易可以涉及到每一天,也就是k >= prices.size() / 2,此时就可以转换为没有交易次数限制的问题
代码如下
class Solution { public: int maxProfit(int k, vector<int>& prices) { /* 如果k很大,采用其他方式 */ if(k >= prices.size() / 2) return quickSolver(prices); vector<vector<int>> states(2, vector<int>(2 * k + 1, 0)); for(int i = 1; i < 2 * k + 1; i += 2) { states[0][i] = INT_MIN; states[0][i + 1] = 0; } int cur = 0, next = 1; for(int i = 0; i < prices.size(); ++i) { /* 把两次变为k次,就需要放在循环里了 */ for(int j = 1; j < 2 * k + 1; j += 2) { states[next][j] = std::max(states[cur][j], states[cur][j - 1] - prices[i]); states[next][j + 1] = std::max(states[cur][j + 1], states[cur][j] + prices[i]); } std::swap(next, cur); } return states[cur][2 * k]; } private: /* 没有交易次数限制,直接遍历,见第二题 */ int quickSolver(vector<int>& prices) { int profit = 0; for(int i = 1; i < prices.size(); ++i) profit += std::max(prices[i] - prices[i - 1], 0); return profit; } };
第一道题的第一种解法比较好,直接遍历一遍同时记录最小值和最大差值,同时也没有额外的空间使用
第二道题不太容易理解,其实对于序列无非递增,递减,无序三种可能,分别观察一下即可
第三第四题主要以动态规划为主
相关文章推荐
- leetcode_122. Best Time to Buy and Sell Stock II 多次买卖股票,求交易的最大利润
- 股票买卖之多次交易最大利润
- 每天一道LeetCode-----一个整数序列,每个元素出现两次,只有一个(两个)出现一次,找到这个(这两个)元素
- 每天一道LeetCode-----计算直方图中最大矩形的面积
- 每天一道LeetCode-----计算给定序列中所有长度为k的滑动窗的最大值集合
- 假如一个数组存储了一个股票,在一天交易窗口内各时间点的股票价格(正整数),只允许一次买入和一次卖出,请提供一个算法,计算出通过买入和卖出可以得到的最大利润
- 每天一道LeetCode-----计算二叉树的最大深度及最小深度,判断二叉树是否是高度平衡二叉树
- 每天一道LeetCode-----计算二叉树的最大路径和,路径只需要从一个节点到达另一个节点,无其他要求
- 假如一个数组存储了一个股票,在一天交易窗口内各时间点的股票价格(正整数),只允许一次买入和一次卖出,请提供一个算法,计算出通过买入和卖出可以得到的最大利润
- 在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2),规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行)。给出一天中的股票变化序列,请写一个程序计算一天可以获得的最大收益。
- 每天一道LeetCode----从数组中选择若干不连续元素使得总和最大
- (LeetCode)Best Time to Buy and Sell Stock -- 求股票买卖的最大利润
- 每天一道LeetCode-----计算最长的元素连续序列长度
- 每天一道LeetCode-----对表达式添加括号并求值,返回所有可能的计算结果
- 关于商品买卖最大收益的问题
- 每天一道LeetCode-----给定二维数组代表海域和岛屿,计算有多少个孤岛
- 每天一道LeetCode-----计算字符串s中有多少个子序列和字符串t相等
- 每天一道LeetCode-----计算给定范围内所有数的与运算结果
- 每天一道LeetCode-----计算一个直方图空隙的容量(如果装水能装多少)
- 每天一道LeetCode-----计算小于n的素数个数