CF865D Buy Low Sell High——反悔贪心堆

做题链接

题面

已知接下来(N)天的股票价格,每天你可以买进一股股票,卖出一股股票,或者什么也不做.(N)天之后你拥有的股

票应为(0),当然,希望这N天内能够赚足够多的钱.

输入:

第一行一个整数天数(N)((2<=N<=300000)).

第二行N个数字(p_1,p_2...p_N)((1<=pi<=10^6)),表示每天的价格.

输出: (N)天结束后能获得的最大利润.

思路

我们可以快速想出一种贪心策略:买入价格最小的股票,在可以赚钱的当天卖出。

显然我们可以发现,上面的贪心策略是错误的,因为我们买入的股票可以等到可以赚最多的当天在卖

出。我们考虑设计一种反悔策略,使所有的贪心情况都可以得到全局最优解。(即设计反悔自动机的反

悔策略)定义(C_{buy})为全局最优解中买入当天的价格, (C_{sell}) 为全局最优解中卖出当天的价格,则:

(C_{sell}-C_{buy}=(C_{sell}-C_{i})+(C_{i}-C_{buy}))

(C_i)为任意一天的股票价格

考虑怎么反悔

我们将每天的价格视为一个个"选项", 压入小根堆中,为了保证买入操作在卖出操作之前,我们从前往

后扫描(p),对于现在的价格 (p_i),如果堆顶元素(p_j) 满足 (p_{j}<p_i) ,那么,我们取出

堆顶,在第(j)天买入股票,在第(i)天卖出股票,此时,我们就可以获得(p_i - p_j) 的收益

然而,如果之后有(p_k)满足(p_k > p_i),辣么,我们当前作出的决策可能并不是最优的,

如何反悔呢?

于是,当我们进行上述操作时,我们将(p_i)也压入堆中,增加一个(p_i)的选项,弹出时,我们相当

于将(p_j)按照(p_i)的价格又买了回来

就比如我们 (4 5 6)这个序列,按照程序我们应该是先买入(4),然后卖出(5)(ans+=1),但是显然后面

在6的时候卖会更优,所以我们为了给电脑留出余地,我们再多保存一个历史的状态,再往队列中压入一

个5,这个时候我们还会在6的时候,卖出,并且(ans+=1),正确

其实就相当于是(ans=(6-5)+(5-4)=6-4)

代码

自己写

原文地址:https://www.cnblogs.com/bangdexuanyuan/p/13953544.html