P2569 [SCOI2010] 股票交易
P2569 [SCOI2010] 股票交易
[SCOI2010] 股票交易
题目描述
最近 \(\text{lxhgww}\) 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。
通过一段时间的观察,\(\text{lxhgww}\) 预测到了未来 \(T\) 天内某只股票的走势,第 \(i\) 天的股票买入价为每股 \(AP_i\),第 \(i\) 天的股票卖出价为每股 \(BP_i\)(数据保证对于每个 \(i\),都有 \(AP_i \geq BP_i\)),但是每天不能无限制地交易,于是股票交易所规定第 \(i\) 天的一次买入至多只能购买 \(AS_i\) 股,一次卖出至多只能卖出 \(BS_i\) 股。
另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔 \(W\) 天,也就是说如果在第 \(i\) 天发生了交易,那么从第 \(i+1\) 天到第 \(i+W\) 天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过 \(\text{MaxP}\)。
在第 \(1\) 天之前,\(\text{lxhgww}\) 手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,\(T\) 天以后,\(\text{lxhgww}\) 想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?
输入格式
输入数据第一行包括 \(3\) 个整数,分别是 \(T\),\(\text{MaxP}\),\(W\)。
接下来 \(T\) 行,第 \(i\) 行代表第 \(i-1\) 天的股票走势,每行 \(4\) 个整数,分别表示 \(AP_i,\ BP_i,\ AS_i,\ BS_i\)。
输出格式
输出数据为一行,包括 \(1\) 个数字,表示 \(\text{lxhgww}\) 能赚到的最多的钱数。
提示
- 对于所有的数据,\(1\leq BP_i\leq AP_i\leq 1000,1\leq AS_i,BS_i\leq\text{MaxP}\)。
DP好题
首先,题目规定第x天交易了那么
第 [x+1,x+w]天都不能交易
也就是说第x天交易后的下一个可交易日是x+1+w所以我们让w++
即对第x天,它的上一个可交易日是x-w
记\(dp[i][j]\)表示在第i天,手上持有j股时的最大收益
显然,我们有:
对于购买:\(dp[i][j]=max(dp[i-w][k]-(j-k)*AP_i)| j-k \le AS_i\)
对于卖出:\(dp[i][j]=max(dp[i-w][k]+(k-j)*BP_i)| k-j \le BS_i\)
但是如果我们真的这么写的话,对于每天的转移我们似乎浪费了很多时间
所以我们考虑单调队列优化:
我们发现右半部分的式子分别可以写成:
\(max(dp[i-w][k]+k*AP_i)-j*AP_i | j-k \le AS_i\)
\(max(dp[i-w][k]+k*AP_i)-j*BP_i | j-k \le BS_i\)
显然,\(max\)外的数字并不影响\(max\)内的单调性
所以我们使用单调队列来维护\(max\)然后直接转移就好了
时间复杂度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;
}

浙公网安备 33010602011771号