1. 程式人生 > 實用技巧 >CF865D Buy Low Sell High

CF865D Buy Low Sell High

題目連結

題目解析

首先,有一個錯誤的貪心策略:我們在能夠賺錢的時候就賣出股票。

基於這個思路,我們有一個錯誤的做法:對於某一天,我們查詢前面沒有用過的價格最小的一天,如果那一天的價格比現在小,就進行一次買入-賣出操作。這個可以用小根堆維護,每次都把股票價格壓入,然後每次找,如果要操作,就彈出。

當然,這個是錯誤的,我們可以把股票留在後面某一次賣出,可能會產生更優的答案。考慮反悔。如果一次交易的賣出價格為\(P_{sell}\),買入價格為\(P_{buy}\),那麼利潤其實可以表示為\(profit=(P_{sell}-P_i)+(P_i-P_{buy})\) \(P_i\)是任意一天的價格,就相當於我們在第\(i\)

天賣出,但是又後悔了,所以又退回去。

注意,執行反悔操作之後,相當於我們第\(i\)天什麼都沒有做,那麼\(i\)還是可以作為買入的那一天的,所以還是要壓入。

還有一個點,是我想了很久的。就是如果我們壓入兩次,那麼在這一天我真的賣了,沒有反悔,而後面我們又把它彈出來作為買入,那麼在這一天我們既買入又賣出了,與題意不符。

注意到事實上不會發生這種情況,因為這樣操作等價於在最前面買入,最後面賣出。


►Code View

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 100005
#define LL long long
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
	return f*x;
}
int n;
LL ans;
priority_queue<int,vector<int>,greater<int> >Q; 
int main()
{
	n=rd();
	for(int i=1;i<=n;i++)
	{
		int p=rd();
		if(!Q.empty())
		{
			int x=Q.top();
			if(x<p)
			{
				Q.pop();
				ans+=p-x;
				Q.push(p);
			}
		}
		Q.push(p);
	}
	printf("%lld\n",ans);
	return 0;
}
/*

*/