P2569 股票交易题解
P2569 股票交易 题解
思路:
容易想到 \(dp\),我们设 \(f_{i,j}\) 表示第 \(i\) 天剩下 \(j\) 张股票最多可以赚到多少钱
显然由于 \(ap\ge bp\) 所以一天之内不可能既买又卖,只能只买/只卖/不卖不卖
不卖不卖的转移十分容易:
\[f_{i,j}=f_{i-1,j}
\]
而只买分为两种情况:凭空买,在前面交易的基础上买
凭空买的话,转移如下:
\[f_{i,j}=-ap_i\times j
\]
在前面交易的基础上买的转移如下:
\[f_{i,j}=\max\{f_{i-w-1,k}-(j-k)\times ap_i\}
\]
卖的话只能在前面交易的基础上卖,转移如下:
\[f_{i,j}=\max\{f_{i-w-1,k}+(k-j)\times bp_i\}
\]
显然,前两个转移操作是 \(\text O(nm)\) 的,可以接受
而后两个转移则是 \(\text O(nm^2)\) 需要优化
对于转移 \(3\) 我们可以正序枚举 \(k\),然后搞一个单调队列维护其单调性,以此来转移
因为
\[f_{i,j}=\max\{f_{i-w-1,k}-(j-k)\times ap_i\}=(f_{i-w-1,k}+k\times ap_i)-j\times ap_i
\]
显然 \(j\times ap_i\) 是一个常数,而前半部分的 \(f_{i-w-1,k}+k\times ap_i\) 只跟 \(k\) 的取值有关,因此我们可以用单调队列来维护这个东西
这样复杂度就由 \(\text O(nm^2)\to \text O(nm)\)
可以接受了
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e3+5;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int f[N][N],q[N];
signed main(){
int n=read(),m=read(),w=read();
memset(f,0xcf,sizeof(f));//初始赚的钱为无穷小
for(int i=1;i<=n;++i){
int ap=read(),bp=read(),as=read(),bs=read();
for(int j=0;j<=as;++j) f[i][j]=-1*j*ap;
for(int j=0;j<=m;++j) f[i][j]=max(f[i][j],f[i-1][j]);
if(i<=w) continue;
int l=1,r=0;
for(int j=0;j<=m;++j){
while(l<=r&&q[l]<j-as) ++l;
while(l<=r&&f[i-w-1][q[r]]+q[r]*ap<=f[i-w-1][j]+j*ap) --r;
q[++r]=j;
if(l<=r)
f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*ap-j*ap);
}
l=1,r=0;
for(int j=m;j>=0;--j){
while(l<=r&&q[l]>j+bs) ++l;
while(l<=r&&f[i-w-1][q[r]]+q[r]*bp<=f[i-w-1][j]+j*bp) --r;
q[++r]=j;
if(l<=r)
f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*bp-j*bp);
}
}
printf("%d",f[n][0]);//全卖掉最小
}