题目大意:有一只股票,假如你能遇见未来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);
}
}