1. 程式人生 > >BZOJ1855 || P2569 [SCOI2010]股票交易【單調佇列優化DP】

BZOJ1855 || P2569 [SCOI2010]股票交易【單調佇列優化DP】

Time Limit: 5 Sec Memory Limit: 64 MB

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能賺到的最多的錢數。

HINT

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

題目分析

dp[i][j]dp[i][j]表示到第ii天時手上恰好有jj張股票的最大收益

那麼有狀態轉移方程

dp[i][j]=-j*ap[i]//賦初值
dp[i][j]=max(dp[i][j],dp[i-1][j])//第i天啥也不幹
dp[i][j]=max(dp[i][j],dp[i-w-1][k]-(j-k)*ap[i])//第i天買入股票,(j-as[i]<=k<j)
dp[i][j]=max(dp[i][j],dp[i-w-1][k]+(k-j)*bp[i])//第i天賣出股票,(j<k<=j+bs[i])

直接轉移複雜度為O(n3)O(n^3),顯然無法承受 嘗試把第3、4個方程中k的列舉省去 以第3個為例 dp[i][j]=max(dp[i

][j],dp[iw1][k](jk)ap[i])=max(dp[i][j],max(dp[iw1][k]+kap[i])jap[i])dp[i][j]=max(dp[i][j],dp[i-w-1][k]-(j-k)*ap[i])=max(dp[i][j],max(dp[i-w-1][k]+k*ap[i])-j*ap[i])

這裡面的max(dp[iw1][k]+kap[i])max(dp[i-w-1][k]+k*ap[i])顯然可以用單調佇列維護 對於第4個方程同理,不過jj的列舉要倒序

這樣複雜度就降到O(n2)O(n^2)

#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
   
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
 
const int maxn=2010;
int n,m,w;
int ap[maxn],bp[maxn];
int as[maxn],bs[maxn];
int dp[maxn<<1][maxn<<1],ans;
int q[maxn],ll,rr;
 
int main()
{
    n=read();m=read();w=read();
    for(int i=1;i<=n;++i)
    ap[i]=read(),bp[i]=read(),
    as[i]=read(),bs[i]=read();
     
    memset(dp,128,sizeof(dp)); dp[0][0]=0;
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<=as[i];++j) dp[i][j]=-1*j*ap[i];
        for(int j=0;j<=m;++j) dp[i][j]=max(dp[i][j],dp[i-1][j]);
         
        if(i-w-1<0) continue;
        ll=rr=1;
        for(int j=0;j<=m;++j)
        {
            while(ll<rr&&q[ll]<j-as[i]) ll++;
            while(ll<rr&&dp[i-w-1][q[rr-1]]+q[rr-1]*ap[i]<=dp[i-w-1][j]+j*ap[i]) rr--;
            q[rr++]=j;
            dp[i][j]=max(dp[i][j],dp[i-w-1][q[ll]]-(j-q[ll])*ap[i]);
        }
          
        ll=rr=1;
        for(int j=m;j>=0;--j)//注意這裡倒敘
        {
            while(ll<rr&&q[ll]>j+bs[i]) ll++;
            while(ll<rr&&dp[i-w-1][q[rr-1]]+q[rr-1]*bp[i]<=dp[i-w-1][j]+j*bp[i]) rr--;
            q[rr++]=j;
            dp[i][j]=max(dp[i][j],dp[i-w-1][q[ll]]+(q[ll]-j)*bp[i]);
        }
    }
    for(int i=0;i<=m;++i)
    ans=max(ans,dp[n][i]);
    printf("%d",ans);
    return 0;
}