[SCOI2010] 股票交易(单调队列优化DP)
作者:互联网
题意
最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。
通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi≥BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BSi股。
另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。
同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过MaxP。
在第1天之前,lxhgww手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T天以后,lxhgww想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?
0≤W<T≤2000
1≤MaxP≤2000
1≤BPi≤APi≤1000
1≤ASi,BSi≤MaxP
分析
dp状态并不难想,以 前i天 作为 阶段,以 第i天结束时持有j股 为 状态,可得:
F(i,j):表示第i天结束时持有j股的最大总收益
共有3种状态转移的情况:
- 第i天不进行任何交易;
- 第i天买入j−k股,由于交易需间隔W天,故对应F(i−W−1,k),因为0<j−k≤ASi,所以j−ASi≤k<j;
- 第i天卖出k−j股,由于交易需间隔W天,故对应F(i−W−1,k),因为0<k−j≤BSi,所以j<k≤j+BSi;
状态转移方程:
F(i,j)=⎩⎪⎪⎨⎪⎪⎧F(i−1,j)j−ASi≤k<jmax{F(i−W−1,k)−(j−k)∗APi}j<k≤j+BSimax{F(i−W−1,k)+(k−j)∗BPi}1≤i≤T,0≤j≤MaxP
初值:F(0,0)=0,其余均为−∞;
目标:0≤j≤MaxPmax{F(T,i)};
直接采用三重循环的时间复杂度会达到O(T⋅MaxP2),故需要对其进行优化,显然状态转移方程的②式和③式满足单调队列优化的条件 (1D/1D动态规划优化),当固定阶段i时,有:
- 决策k的上下边界是关于状态j的一次函数;
- 除F(i−W−1,k)外的冗余部分是关于j和k的一次函数
以②式为例,F(i,j)=j−ASi≤k<jmax{F(i−W−1,k)−(j−k)∗APi}
可转化为:F(i,j)=j−ASi≤k<jmax{G(i,k)}−j∗APi,其中G(i,k)=F(i−W−1,k)+k∗APi;
对于 任意j,第一部分的值是相等的,最优决策k只需要满足在范围内即可,故只需要维护 一个G(i,k)递减,k∈[j−ASi,j)的单调队列 即可。
对③式做相同处理即可,注意处理下标越界情况,最后总的时间复杂度度为O(T⋅MaxP)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
const int maxn=2e3+10;
int T,MaxP,W;
int AP[maxn],BP[maxn],AS[maxn],BS[maxn];
int F[maxn][maxn];
int GA(int i,int k)
{
return F[max(0,i-W-1)][k]+k*AP[i];
}
int GB(int i,int k)
{
return F[max(0,i-W-1)][k]+k*BP[i];
}
int main()
{
scanf("%d %d %d",&T,&MaxP,&W);
for(int i=1;i<=T;i++)
scanf("%d %d %d %d",&AP[i],&BP[i],&AS[i],&BS[i]);
memset(F,0xcf,sizeof(F));
F[0][0]=0;
for(int i=1;i<=T;i++)
{
//第i天不交易
for(int j=0;j<=MaxP;j++)
F[i][j]=F[i-1][j];
//第i天买入j-k股
deque<int> q;
for(int j=0;j<=MaxP;j++)
{
while(!q.empty()&&q.front()<j-AS[i]) //保证下界j-AS[i]
q.pop_front();
if(!q.empty())
F[i][j]=max(F[i][j],GA(i,q.front())-j*AP[i]);
while(!q.empty()&&GA(i,q.back())<=GA(i,j))
q.pop_back();
q.push_back(j); //新加入的k一定小于j,保证了上界j
}
//第i天卖出k-j股
q.clear();
for(int k=1;k<=min(MaxP,BS[i]);k++)
{
while(!q.empty()&&GB(i,q.back())<=GB(i,k))
q.pop_back();
q.push_back(k);
}
for(int j=0;j<=MaxP;j++)
{
while(!q.empty()&&q.front()<=j) //保证下界j
q.pop_front();
if(!q.empty())
F[i][j]=max(F[i][j],GB(i,q.front())-j*BP[i]);
if(j+BS[i]+1<=MaxP)
{
while(!q.empty()&&GB(i,q.back())<=GB(i,j+BS[i]+1))
q.pop_back();
q.push_back(j+BS[i]+1); //新加入的k一定小于等于j+BS[i],保证了上界j+BS[i]
}
}
}
int ans=0;
for(int j=0;j<=MaxP;j++)
ans=max(ans,F[T][j]);
printf("%d\n",ans);
return 0;
}
墓华
发布了214 篇原创文章 · 获赞 40 · 访问量 2万+
私信
关注
标签:le,队列,max,MaxP,int,ASi,iii,DP,SCOI2010 来源: https://blog.csdn.net/Ratina/article/details/104101607