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\)
代码
自己写