188. Best Time to Buy and Sell Stock IV
来源:互联网 发布:花生壳 该域名被锁定 编辑:程序博客网 时间:2024/06/10 06:35
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most k transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
这题主要参考了leetcode discuss 区的高分解法。
方法一:
我们知道,当k足够大,这个array能产生的最大利润就是每一个单独的valley-peak对的利润总和。当k有限制,就会涉及到相邻valley-peak对的合并问题。假设相邻的两队分别为,[v1, p1]和[v2, p2]. 如果v2小于v1,那么肯定不会有合并产生,因为不管p1, p2什么样,都不会有产生更大的组合。当p2小于p1, 也没有合并的意义。所以这里有一很聪明的做法,就是在处理这种合并问题上,把多个单独的区间的和看成一个总的区间和几个补余。例如,当v2大于v1, p2大于p1, 我们可以将p1-v1+p2-v2存成p2-v1加上p1-v2。
class Solution {public: // We can find all adjacent valley/peak pairs and calculate the profits easily. // Instead of accumulating all these profits like Buy&Sell Stock II, we need // the highest k ones. // // The key point is when there are two v/p pairs (v1, p1) and (v2, p2), satisfying // v1 <= v2 and p1 <= p2, we can either make one transaction at [v1, p2], or make // two at both [v1, p1] and [v2, p2]. The trick is to treat [v1, p2] as the first // transaction, and [v2, p1] as the second. Then we can guarantee the right max // profits in both situations, p2 - v1 for one transaction and p1 - v1 + p2 - v2 // for two. // // Finding all v/p pairs and calculating the profits takes O(n) since there are // up to n/2 such pairs. And extracting k maximums from the heap consumes another O(k*log(n)). int maxProfit(int k, vector<int> &prices) { int ret = 0; int n = prices.size(); int v = 0; // valley index int p = 0; // peak index + 1; vector<int> profits; stack<pair<int, int>> vp_pairs; while (p < n) { // Find next valley/peak pair. for (v = p; (v < n - 1) && (prices[v] >= prices[v + 1]); v++); for (p = v + 1; (p < n) && (prices[p] >= prices[p - 1]); p++); // Save profit of 1 transaction at last v/p pair, if current v is lower than last v. while (!vp_pairs.empty() && (prices[v] < prices[vp_pairs.top().first])) { profits.push_back(prices[vp_pairs.top().second - 1] - prices[vp_pairs.top().first]); vp_pairs.pop(); } // Save profit difference between 1 transaction (last v and current p) and 2 transactions // (last v/p + current v/p), if current v is higher than last v and current p is higher // than last p. while (!vp_pairs.empty() && (prices[p - 1] >= prices[vp_pairs.top().second - 1])) { profits.push_back(prices[vp_pairs.top().second - 1] - prices[v]); v = vp_pairs.top().first; vp_pairs.pop(); } vp_pairs.push(pair<int, int>(v, p)); } // Save profits of the remaining v/p pairs. while (!vp_pairs.empty()) { profits.push_back(prices[vp_pairs.top().second - 1] - prices[vp_pairs.top().first]); vp_pairs.pop(); } if (k >= profits.size()) { // Since we have no more than k profit pairs, the result is the sum of all pairs. ret = accumulate(profits.begin(), profits.end(), 0); } else { // Move the k highest profits to the end and the average time complexity should be O(n). nth_element(profits.begin(), profits.begin() + profits.size() - k, profits.end()); // Sum up the k highest profits. ret = accumulate(profits.begin() + profits.size() - k, profits.end(), 0); } return ret; }};
这里还有个值得注意的操作,原po给出的代码,将profit存在一个max heap里,用priority queue实现,最后依次导出前k大的数据。复杂度为O(nlogn + klogn)。但事实上可以改进为上述模式,我们只需知道前k个的和,至于顺序无所谓,所以实际上是个kth partition问题,不需要O(nlogn)来维护一个heap,可以用O(n)找出第k大的数就行,这里有stl的nth_element可以用。十分方便。
//———————————————————————————————–
STL 简记:
accumulate:
int main(){ std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = std::accumulate(v.begin(), v.end(), 0); int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());}
accumulate可以方便的用来做连续的求和和乘积。如上述代码所示。前两个参数指明起始位置,第三个参数是initial value。第二个参数可以用来指明是加法(默认)还是乘法。
nth_element:
int main(){ std::vector<int> v{5, 6, 4, 3, 2, 6, 7, 9, 3}; std::nth_element(v.begin(), v.begin() + v.size()/2, v.end()); std::cout << "The median is " << v[v.size()/2] << '\n'; std::nth_element(v.begin(), v.begin()+1, v.end(), std::greater<int>()); std::cout << "The second largest element is " << v[1] << '\n';}
也就是之前leetcode里的第k大的数的标准调用函数。基于partition算法,复杂度O(n).
参数需要指明,起始位置,第几个,终点位置。
//——————————————————————————————-
方法二:
这题更通用的解法是dynamic programming。代码如下:
int maxProfit(int k, vector<int>& prices) { int len = prices.size(); if (len <= 1) return 0; if (k >= len / 2) return quicksolve(prices); vector<vector<int>> profit(k + 1, vector<int>(len, 0)); for (int i = 1; i <= k; i++) { int balance = -prices[0]; for (int j = 1; j < len; j++) { profit[i][j] = max(profit[i][j - 1], balance + prices[j]); balance = max(balance, profit[i - 1][j - 1] - prices[j]); } } return profit[k][len - 1];}int quicksolve(vector<int>& prices) { int len = prices.size(); int maxprofit = 0; for (int i = 1; i < len; i++) { if (prices[i] > prices[i - 1]) { maxprofit += prices[i] - prices[i - 1]; } } return maxprofit;}
dp的最基本思想是对新的价格,要么买进,要么卖出。我们来看profit[i][j]的产生。profit[i][j]代表在0到j个prices之内做i次交易能产生的最大利润。如果这一点买入j的话,不可能是一个增加profit的选项,所以profit[i][j]要么等于profit[i][j-1],就是j这一天什么都不做。要么就是在这一天把j卖出。可是卖出我们并不知道对应哪一次买入。如何卖出才能维护一个最大利润了?我们需要记录当前最大的balance。balance的更新取决于每一个的买入。是想,本来有一个balance在那,新的一天出现的时候,balance要么就不变,要么就是在这一天产生新的买入,也就是profit[i-1][j-1] - prices[j]. 所以balance的存在就是为了下一次卖出做准备的,就是记录当前卖出之前的,最划算的买入时机。
- [leetcode] 188.Best Time to Buy and Sell Stock IV
- [leetcode] 188.Best Time to Buy and Sell Stock IV
- [leetcode] 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- [LeetCode] 188. Best Time to Buy and Sell Stock IV
- Leetcode 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- 【leetcode】188. Best Time to Buy and Sell Stock IV
- LeetCode 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- Leetcode 188. Best Time to Buy and Sell Stock IV
- Leetcode 188. Best Time to Buy and Sell Stock IV
- 188. Best Time to Buy and Sell Stock IV
- C语言:递归
- spark on yarn
- LINUX目录和文件各自的权限说明,以及目录和文件权限之间的关系(应用:配置linux下上传图片的存储目录)
- Python学习手册
- MVC模式与三层架构
- 188. Best Time to Buy and Sell Stock IV
- 特斯拉首次正面回应在华建厂事宜!别高兴太早,独资建厂的特斯拉给不了你白菜价
- ICCV2017 | 一文详解GAN之父Ian Goodfellow 演讲《生成对抗网络的原理与应用》(附完整PPT)
- 资源 | 最新机器学习必备十大入门算法!都在这里了
- 观点 | 转行人士如何在人工智能领域保持一定的竞争力?
- C语言_三子棋
- My favorite #golang retry function
- mac版pycharm修改当前项目所用的python 版本
- jquery.lazyload.js 懒加载