「SCOI2010」股票交易

单调队列是个好东西。

传送门

洛谷P2569

BZOJ1855

题解

很容易想到一个DP:

$F[i][j]$表示到了第$i$天结束时,手上有$j$只股票,能赚到的最多的钱。

直接暴力转移需要枚举上上一次交易的时间和上一次交易后手上的股票数,这样时间复杂度时$\Theta(n^4)$的。

但是如果枚举到每一个状态时都加上一句$F[i][j]=max(F[i][j],F[i-1][j]);$的话,这样只要从第$i-W-1$天转移过来就行,复杂度降到$\Theta(n^3)$。

仔细思考之后不难发现,对于所有状态$F[i-W-1][j]$,把手上的股票按照第$i$天的价格来折算成钱之后,可以通过一个单调队列来找最优的状态来转移,然后就优化到了$\Theta(n^2)$。可以过了。

代码

蒟蒻我写单调队列的方式有点奇葩,所以特别丑Q$\omega$Q。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2005,inf=0x3F3F3F3F;
int n,m,W,AP[maxn],BP[maxn],AS[maxn],BS[maxn],que[maxn],F[maxn][maxn];
inline int read()
{
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
return ret*f;
}
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(F,192,sizeof(F));
for(int i=1;i<=W+1;i++)
for(int j=0;j<=m;j++)
F[i][j]=max(F[i-1][j],j<=AS[i]?-AP[i]*j:-inf);
for(int i=W+2;i<=n;i++)
{
int hed=0,til=1;
for(int j=m;j>=m-AS[i];j--)
{
while(hed>=til&&F[i-W-1][j]>=F[i-W-1][que[hed]]+(que[hed]-j)*AP[i]) hed--;
que[++hed]=j;
}
for(int j=m;j>=0;j--)
{
while(til<=hed&&que[til]>j) til++;
F[i][j]=max(F[i][j],F[i-1][j]);
if(j<=AS[i]) F[i][j]=max(F[i][j],-j*AP[i]);
F[i][j]=max(F[i][j],F[i-W-1][que[til]]-AP[i]*(j-que[til]));
if(j>AS[i])
{
while(hed>=til&&F[i-W-1][j-1-AS[i]]>=F[i-W-1][que[hed]]+(que[hed]-(j-1-AS[i]))*AP[i]) hed--;
que[++hed]=j-1-AS[i];
}
}
hed=0;til=1;
for(int j=0;j<=BS[i];j++)
{
while(hed>=til&&F[i-W-1][j]>=F[i-W-1][que[hed]]-(j-que[hed])*BP[i]) hed--;
que[++hed]=j;
}
for(int j=0;j<=m;j++)
{
while(til<=hed&&que[til]<j) til++;
F[i][j]=max(F[i][j],F[i-W-1][que[til]]+BP[i]*(que[til]-j));
if(j+BS[i]<m)
{
while(hed>=til&&F[i-W-1][j+1+BS[i]]>=F[i-W-1][que[hed]]-(j+1+BS[i]-que[hed])*BP[i]) hed--;
que[++hed]=j+1+BS[i];
}
}
}
printf("%d\n",F[n][0]);
return 0;
}