洛谷/Codeforces CF865D 題解

若想要深刻學習反悔貪心,傳送門html


Description:c++

已知接下來 \(n\) 天的股票價格,天天能夠買入當天的股票,賣出已有的股票,或者什麼都不作,求 \(n\) 天以後最大的利潤。學習

Methodspa

咱們能夠快速想出一種貪心策略:買入價格最小的股票,在能夠賺錢的當天賣出。設計

顯然咱們能夠發現,上面的貪心策略是錯誤的,由於咱們買入的股票能夠等到能夠賺最多的當天在賣出。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;
}
相關文章
相關標籤/搜索