中级农民
- 积分
- 136
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2015-1-20
- 最后登录
- 1970-1-1
|
本帖最后由 stellari 于 2015-7-4 16:32 编辑
我给一个思路吧(虽然还不能严格证明正确性)。希望抛砖引玉:
首先,我们肯定不会在非“谷值”的地方买入,也不会在非“峰值”的地方卖出。所以,为了简化问题,我们先提取出这个序列中所有的“谷值”和“峰值”。分别存放于队列A和B中。
接下来,这个问题和Stock II的唯一区别就在于每次交易要收取佣金,所以使得有时候多次连续交易来得不如合并成一次划算。比如[1->20, 18->30]两次交易=>19-3 + 12-3 = 25,就不如[1->30]一次交易=>30-1-3=26来得划算。
假设第i次交易的始终价格分别为(Ai, Bi),而第i+1次交易为(Ai+1, Bi+1);那么,
当两交易的profit都>=3时,
分开交易的收益是
Bi+1-Ai+1 + Bi-Ai - 6而合起来交易的收益是
Bi+1-Ai -3
所以,如果两次交易应该合并,则必有:
Bi+1-Ai -3 >= Bi+1-Ai+1 + Bi-Ai - 6
即
Bi - Ai+1 <= 3 ....... (a)
如果仅有第二个交易的profit小于3,那么上述条件(a)是不充分的,比如[1 -> 100],[98 -> 99]这种情况本来不应该合并。所以,此时还应检查是否有
Bi+1 >= Bi ...... (b)
如果仅有第一个交易的profit小于3,那么(a), (b)也是不充分的,比如[2 -> 3], [1 -> 100]这种情况不应合并。所以,此时应检查是否有
Ai+1 >= Ai ...... (c)
如果两个交易的profit都<=3,那么此时无论两个交易的始末值如何,总是可以合并。因为,如果合并后的profit依然<3,甚至变为负值,那么我们可以在最后的postprocessing步骤忽略掉这次交易;如果合并后的profit>3,那么我们本来就应该合并这两次交易。所以合并总是正确的选择。
综上所说,我们能够合并两次交易的充分条件是(a) & (b) & (c).
所以,我们依次取出A和B中每一对元素(Ai, Bi),检查它们和上一对元素(Ai-1, Bi-1)是否满足这个关系。如果是的话,则合并二者为(Ai-1, Bi),不是的话则二者均保留。然后继续检查下一对元素。
在合并时,每个范围最多会被合并一次。比如(A3, B3)如果已经被合并到(A4, B4)中去,那么以后就不会再单独检查(A3, B3)了。相当于每个范围最多被删除一次。所以最后总时间应该仍然是O(N)。
最后,统计没有被合并的范围的个数。如果其中出现profit<3的交易,则认为其profit为0(即不进行此次交易)。
代码如下:- int getMaxProfitV(vector<int>& prices)
- {
- int N = prices.size();
- queue<int> A, B;
- // 找到所有峰值和谷值
- for (int i = 0; i < N; ++i) {
- int left = (i == 0)? INT_MAX: prices[i-1];
- int right = (i == N-1)? INT_MIN: prices[i+1];
- if (left <= prices[i] && prices[i] > right) {
- B.push(prices[i]);
- }
- else if (left > prices[i] && prices[i] <= right) {
- A.push(prices[i]);
- }
- }
- stack<pair<int, int> > res;
- res.push(pair<int, int>(A.front(), B.front()));
- A.pop(); B.pop();
- // 维护一个stack。每次新处理一次交易,就试图将此次交易与栈顶的交易合并,直到不能合并为止。
- while (!A.empty()) {
- int lo = A.front(), hi = B.front();
- A.pop(); B.pop();
- // 虽然是二重循环,但是push操作只进行了N次,所以pop操作也最多只能进行O(N)次。最后总时间依然是O(N)。
- // (a), (b), (c) 三条件必须都满足
- while (!res.empty() &&
- res.top().first <= lo &&
- res.top().second - lo <= 3 &&
- res.top().second <= hi
- ) {
- lo = res.top().first;
- res.pop();
- }
- res.push(pair<int, int>(lo, hi));
- }
- int sum = 0;
- // 最后统计所有的范围,<3的则看做0.
- while(!res.empty()) {
- pair<int, int> cur = res.top(); res.pop();
- cout << cur.first << ", " << cur.second << endl;
- if (cur.second - cur.first > 3) {
- sum += cur.second - cur.first - 3;
- }
- }
- return sum;
- }
复制代码 代码看起来有点丑是真的。我猜这题应该也可以通过类似于其他stock题那样用DP来做,不过我还没想到具体解法。 |
|