[BZOJ 1855][SCOI 2010]股票交易(單調佇列優化DP)
阿新 • • 發佈:2019-01-24
題目連結
思路
很顯然是個DP題,比較容易想到下面的DP做法:
用
而DP方程則為(以轉移情況為購入股票舉例)
這樣做的正確性是顯然的,
變化一下這個DP方程:
一般可以用單調佇列優化的DP都能滿足形如 [i][j]
而賣出的做法也非常相似,在這裡就不再贅述了
程式碼
剛開始陣列開大了,直接卡成TLE。。。鬱悶。。。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 2100
#define INF 0x3f3f3f3f
using namespace std;
pair<int,int >q[MAXN];
int f[MAXN][MAXN];
int T,maxP,w;
int main()
{
int ans=-INF;
memset(f,-INF,sizeof(f));
scanf("%d%d%d",&T,&maxP,&w);
for(int i=1;i<=T;i++)
{
int ap,bp,as,bs;
scanf("%d%d%d%d",&ap,&bp,&as,&bs);
for(int j=0;j<=as;j++) f[i][j]=-ap*j; //初始化f[i][j]=-ap*j(光買了股票)
for(int j=0;j<=maxP;j++) f[i][j]=max(f[i][j],f[i-1][j]); //初始化什麼都沒買的情況更新f陣列
int k=i-w-1; //單調佇列裡儲存的就是(j,f[w][j]+ap[i]*j(-bp[i]*j))這個二元組
if(k>=0)
{
int h=0,t=0;
for(int j=0;j<=maxP;j++)
{
//買入股票
while(h<t&&q[h].first<j-as) h++;
while(h<t&&q[t-1].second<=f[k][j]+ap*j) t--;
q[t++]=make_pair(j,f[k][j]+ap*j);
if(h<t) f[i][j]=max(f[i][j],q[h].second-ap*j);
}
h=0,t=0;
for(int j=maxP;j>=0;j--)
{
//賣出股票
while(h<t&&q[h].first>j+bs) h++; //!!!!!
while(h<t&&q[t-1].second<=f[k][j]+bp*j) t--;
q[t++]=make_pair(j,f[k][j]+bp*j);
if(h<t) f[i][j]=max(f[i][j],q[h].second-bp*j);
}
}
ans=max(ans,f[i][0]); //顯然以某天交易結束後收手不幹,肯定是手上沒有股票是最優的
}
printf("%d\n",ans);
return 0;
}