P2569 [SCOI2010] 股票交易
P2569 [SCOI2010] 股票交易
[SCOI2010] 股票交易
题目描述
最近
通过一段时间的观察,
另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔
在第
输入格式
输入数据第一行包括
接下来
输出格式
输出数据为一行,包括
提示
- 对于所有的数据,
。
DP好题
首先,题目规定第x天交易了那么
第 [x+1,x+w]天都不能交易
也就是说第x天交易后的下一个可交易日是x+1+w所以我们让w++
即对第x天,它的上一个可交易日是x-w
记
显然,我们有:
对于购买:
对于卖出:
但是如果我们真的这么写的话,对于每天的转移我们似乎浪费了很多时间
所以我们考虑单调队列优化:
我们发现右半部分的式子分别可以写成:
显然,
所以我们使用单调队列来维护
时间复杂度O(TW)
然后这题就做完了 (经典结束语)
Code
#include<bits/stdc++.h> #define int long long const int N=2005; const int inf=1e17; using namespace std; int n,m,w; int dp[N][N]; int q[N<<1]; void init() { for(int i=0;i<N;i++) { for(int j=0;j<N;j++) { dp[i][j]=-inf; } } } void work() { init(); cin>>n>>m>>w; w++; for(int i=1,ap,bp,as,bs;i<=n;i++) { scanf("%lld%lld%lld%lld",&ap,&bp,&as,&bs); for(int j=0;j<=as;j++)dp[i][j]=-1ll*ap*j; for(int j=0;j<=m;j++)dp[i][j]=max(dp[i][j],dp[i-1][j]); //copy if(i-w<1)continue; int st=1,ed=0; for(int j=0;j<=m;j++)//之前持有k,现在买j-k使现在有j: { while(st<=ed){if(j-q[st]>as)st++;else break;}//删掉购买数过多的 while(st<=ed){if(dp[i-w][q[ed]]+q[ed]*ap<=dp[i-w][j]+j*ap)ed--;else break;}//优化之前买的少还赚的少的 q[++ed]=j; if(st<=ed)dp[i][j]=max(dp[i][j],dp[i-w][q[st]]+q[st]*ap-j*ap); } st=1,ed=0; for(int j=m;j>=0;j--)//之前持有k,现在卖出k-j使现在有j: { while(st<=ed){if(q[st]-j>bs)st++;else break;}//删掉卖出数过多的 while(st<=ed){if(dp[i-w][q[ed]]+q[ed]*bp<=dp[i-w][j]+j*bp)ed--;else break;}//优化之前买的少还赚的少的 q[++ed]=j; if(st<=ed)dp[i][j]=max(dp[i][j],dp[i-w][q[st]]+q[st]*bp-j*bp); } } int ans=0; for(int i=0;i<=m;i++) { ans=max(ans,dp[n][i]); } printf("%lld",ans); } #undef int int main() { work(); return 0; }