若想要深刻學習反悔貪心,傳送門。html
Description:c++
已知接下來 \(n\) 天的股票價格,天天能夠買入當天的股票,賣出已有的股票,或者什麼都不作,求 \(n\) 天以後最大的利潤。學習
Method:spa
咱們能夠快速想出一種貪心策略:買入價格最小的股票,在能夠賺錢的當天賣出。設計
顯然咱們能夠發現,上面的貪心策略是錯誤的,由於咱們買入的股票能夠等到能夠賺最多的當天在賣出。code
咱們考慮設計一種反悔策略,使全部的貪心狀況均可以獲得全局最優解。(即設計反悔自動機的反悔策略)htm
定義 \(C_{buy}\) 爲全局最優解中買入當天的價格, \(C_{sell}\) 爲全局最優解中賣出當天的價格,則:
\[ C_{sell}-C_{buy}=\left(C_{sell}-C_i\right)+\left(C_i-C_{buy}\right) \]blog
\(C_i\) 爲任意一天的股票價格。ip
即咱們買價格最小的股票去賣價格最大的股票,以期獲得最大的利潤。咱們先把當前的價格放入小根堆一次(此次是以上文的貪心策略貪心),判斷當前的價格是否比堆頂大,如果比其大,咱們就將差值計入全局最優解,再將當前的價格放入小根堆(此次是反悔操做)。至關於咱們把當前的股票價格若不是最優解,就沒有用,最後能夠獲得全局最優解。element
上面的等式即被稱爲反悔自動機的反悔策略,由於咱們並無反覆更新全局最優解,而是經過差值消去中間項的方法快速獲得的全局最優解。
(假如尚未理解這道題,能夠看一看代碼,有詳細的註釋)
Code:
#include<bits/stdc++.h> #define int long long using namespace std; inline void read(int &x) { int f=1;x=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} x*=f; } priority_queue<int,vector<int>,greater<int> >qu;//開一個小根堆 int n; int ans=0;//全局最優解 signed main() { read(n); ans=0; for(int i=1,x;i<=n;i++) { read(x);//當前的股票價格 qu.push(x);//貪心策略:買價格最小的股票去買價格最大的股票 if(!qu.empty()&&qu.top()<x)//假如當前的股票價格不是最優解 { ans+=x-qu.top();//將差值計入全局最優解 qu.pop();//將已經統計的最小的股票價格丟出去 qu.push(x);//反悔策略:將當前的股票價格再放入堆中,即記錄中間變量(等式中間無用的Ci) } } printf("%lld\n",ans);//輸出全局最優解 return 0; }