1. 程式人生 > >【DP計劃】11.3——[BZOJ]股票交易(單調佇列優化DP)MEDIUM

【DP計劃】11.3——[BZOJ]股票交易(單調佇列優化DP)MEDIUM

Description 最近lxhgww又迷上了投資股票,通過一段時間的觀察和學習,他總結出了股票行情的一些規律。 通過一段時間的觀察,lxhgww預測到了未來T天內某隻股票的走勢,第i天的股票買入價為每股APi,第i天的股票賣出價為每股BPi(資料保證對於每個i,都有APi>=BPi),但是每天不能無限制地交易,於是股票交易所規定第i天的一次買入至多隻能購買ASi股,一次賣出至多隻能賣出BSi股。 另外,股票交易所還制定了兩個規定。為了避免大家瘋狂交易,股票交易所規定在兩次交易(某一天的買入或者賣出均算是一次交易)之間,至少要間隔W天,也就是說如果在第i天發生了交易,那麼從第i+1天到第i+W天,均不能發生交易。同時,為了避免壟斷,股票交易所還規定在任何時間,一個人的手裡的股票數不能超過MaxP。 在第1天之前,lxhgww手裡有一大筆錢(可以認為錢的數目無限),但是沒有任何股票,當然,T天以後,lxhgww想要賺到最多的錢,聰明的程式設計師們,你們能幫助他嗎? Input 輸入資料第一行包括3個整數,分別是T,MaxP,W。 接下來T行,第i行代表第i-1天的股票走勢,每行4個整數,分別表示APi,BPi,ASi,BSi。 Output 輸出資料為一行,包括1個數字,表示lxhgww能賺到的最多的錢數。 Sample Input 5 2 0

2 1 1 1

2 1 1 1

3 2 1 1

4 3 1 1

5 4 1 1

Sample Output 3

HINT 對於30%的資料,0 < =W 對於50%的資料,0 < =W 對於100%的資料,0 < =W 對於所有的資料,1 < =BPi < =APi < =1000,1 < =ASi,BSi < =MaxP

想要寫出這道題的狀態轉移方程是很簡單的。
f[i][j]表示過了i天,手裡還剩j股股票,所可以賺到的最大值,那麼
f[i][j]=f[i-1][j]   //一股不買也不賣
f[i][j]=max(f[i][j],f[i-W-1][k]-Api*(k-j))  //k∈[j,j+Asi]   //買入一些股
f[i][j]=max(f[i][j],f[i-W-1][k]+Bpi*(j-k))  //k∈[j-Bsi,j]   //賣出一些股
那麼最後的狀態就是f[T][0]f[T][0],顯然最後一天要賣出所有股才優。
那麼這樣的轉移是O(n3)O(n^3)的,我們可以列舉i,ji,j但是對於kk,我們可以進行單調佇列維護,我們維護一個遞減的單調佇列,如果當前隊頭所在的位置已經不在當前區間裡了,那麼隊頭就後移一位。直到對頭在當前區間裡。由於單調遞減,所以最優答案就是從目前最新的隊頭轉移而來。這樣優化之後複雜度就變成O(n2)O(n^2)的了。
轉移完之後要插入,那麼顯然當前的值減去(或加)它們之間的買入賣出差(因為位於靠後的位置迴避當前位置多買或賣一些股)依舊大一隊尾,那就將隊尾向左移一位,來維護佇列單調遞減的性質。
(維護隊尾這一段可能有點玄乎,因為不是特別好表達,具體看程式碼)
#include<bits/stdc++.h>
#define MAXN 2005
#define ll long long
using namespace std;
ll read(){
    char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll h,t,T,MaxP,W,q[2005],f[2005][2005];
struct node{
    ll ap,bp,as,bs;
}F[MAXN];
int main()
{
    T=read();MaxP=read();W=read();
    for(ll i=1;i<=T;i++){
        F[i]=(node){read(),read(),read(),read()};
    }
    memset(f,~0x3f,sizeof(f));f[0][0]=0;
    for(int i=1;i<=W;i++){
        for(int j=0;j<=MaxP;j++) f[i][j]=f[i-1][j];
        for(int j=1;j<=F[i].as;j++) f[i][j]=max(f[i][j],-j*F[i].ap);
    }
    for(ll i=W+1;i<=T;i++){
        for(ll j=0;j<=MaxP;j++) f[i][j]=f[i-1][j];
        h=1;t=0;q[0]=0;
        for(ll j=0;j<=MaxP;j++){
            while(h<=t&&q[h]<j-F[i].as) h++;
            if(h<=t) f[i][j]=max(f[i][j],f[i-W-1][q[h]]-F[i].ap*(j-q[h]));
            while(h<=t&&f[i-W-1][j]>f[i-W-1][q[t]]-F[i].ap*(j-q[t])) t--;
            q[++t]=j;
        }
        memset(q,0,sizeof(q));h=1,t=0;q[0]=MaxP+1;
        for(ll j=MaxP;j>=0;j--){
            while(h<=t&&q[h]>j+F[i].bs) h++;
            if(h<=t) f[i][j]=max(f[i][j],f[i-W-1][q[h]]+F[i].bp*(q[h]-j));
            while(h<=t&&f[i-W-1][j]>f[i-W-1][q[t]]+F[i].bp*(q[t]-j)) t--;
            q[++t]=j;
        }
    }
    printf("%lld",f[T][0]);
    return 0;
}