Codeforces 867E :Buy Low Sell High 问题(贪心、优先队列)

题目大意:有一只股票,假如你能遇见未来N天,每天的股票价格。问最多获利多少?

(在任意一天内,只能买或卖或者不操作,当然手中未有股票的时候不能卖)
这题跟普通促销问题不一样的是,他可以不买不卖。

此题用到的贪心算法: 只要当前股价大于之前所买的股票价格,咱就卖。
很容易想到的是,把所有价格排序,更大的一些相加减去剩下更小的一些相加。
可难以操作的是,时间是不可逆的,如果前一天卖10块钱,后一天卖8块钱,我总不可能先买后一天的股票吧?
所以根据贪心,这边的思路就是:每次输入当天股价时,如果这个股价大于之前所购买的最小值(假设之前全部购买),那我赚到的差价就是最大的了
那我如何能存储之前每天的股价,并且每次与最低价作对比呢(可以用*min_element出数组最小元素,在这里也可以用新知识---- 优先队列 )
优先队列: 将每天的股价压入(这个队列所模拟的就是,假设我每天都购买股票,那么往后,一遇到比之前所购买的股价更高的,这个队顶元素(优先队列队顶为队列最小元素)删除(相当于真正的买了它,然后以高价卖出那么我就可以卖掉,赚差价了)
不过这里存在一个问题,比如:

例子: 2 5 9 6

将2压入队列,由于5>2,卖出,队顶2清除(升序优先队列取队列中最小元素放置队顶),差价为3。可是第三天可以卖9块钱,这样第二天不卖,第三题才卖的话,差价为7,赚得更多。
这里看到别的文章给出了一个思路,如果5>2,则2出队,压入两次5:
第一次压入5:
真正意义上地买下了 2 ,出售的价格为 N(此时只卖到 5 ) ,其买入2后出售的总盈利sum+=N - 2,此时N等于 5 (N为最后真正需要出售的价格,即这例子中的 9 ,只不过现在只遍历到了 5 ),sum已经等于了 5-2=3 。这时 2 出队,5 进队,相当于2被5所替换掉(买入的2升值为 5 )。这样做会使得:即便之后,9>5(此时5为队顶元素),5出队,sum+= 9-5,此时sum仍为9。 这是因为相当于:将2卖到9分成两部分了,先将2卖到5,再将5卖到9,其总差值是不变的。这样做就可以保证了 2 卖到 9 !

第二次压入5
是为了:假设我这次也是买,看看后面有没有比5大的 【当然这时队列中有两个 5 ,逻辑上一个是为了卖掉 2 那只(即本例中的 9 - 2),如果之后还有比5大的,那么就购买这里的 5 ,那边卖掉(即本例中的 6 - 5)。当然即使9与6对调sum也是不变的】。 如果只有一个比5大的(即没有6),那么相当于只用了 9 买了 2 ;如果没有一个大于5的(即只有 2 5),那么相当于只用了 5 买了 2 。
当然如果当前num并未大于队顶元素的话,就假设买下它,同时也就是加入队列中。 因为如果之后还有大于num的股价且这时候num成为了队顶,那么就要买它了;如果不需要用到它 【即遍历完了后,它还不是队顶(即会有比它还小的股价,买入这个更小的);或者之后没有一个股价大于它,那就不操作它(不然就赔了)】

如同上面所述,这样循环式地到第N个数判断完后,sum就是最大盈利值了,这里是sum= (9-2)+(6-5)= 8 。
下面是代码,各步骤还有一些解释:

#include<iostream>
#include<algorithm>
#include<queue>
#include<functional>
using namespace std;
long long int n, num;
long long int sum;
int main()
{
	while (scanf_s("%lld", &n) != EOF)
	{
		sum = 0;
		priority_queue <int, vector<int>, greater<int>> q;//  greater相当于队列中从上到下升序,这个优先队列会使得每次队顶元素都会是此队列中最小的元素(这里是创建了一个升序优先队列,可以去搜下如何定义)
		while (!q.empty())
		{
			q.pop();
		}//可能系统分配的这个队列空间中有未初始化区域,这样可以达到循环清空的效果
		while (n--)
		{
			scanf_s("%lld", &num);
			if (&&!q.empty()&&num > q.top()) //如果栈中有元素(即手中有股票的话)且当前股价大于之前股价最小值(假设购买的)
			{
				sum += num - q.top();//买入这个最小值,此时总盈利暂时为当前的 num - q.top()。即可能之后还会有比num更大的股价(原例子中的 9 ) 
				q.pop();//清除这个最小值,已经被买入,不能干扰下次买入,不然会重复购买
				q.push(num);//相当于使这个最小值升值为num(原例子中的 2-> 5 )
				q.push(num);//将每次的价格压入队列中,假设我购买了它(原例子中:为了达到 6 - 5 的目的)
			}
			else
				q.push(num);//将每次的价格压入队列中,假设我购买了它
		}
		printf("%lld\n", sum);
	}
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值