【HDU3401】Trade-单调队列优化DP

来源:互联网 发布:翁其钊 知乎 编辑:程序博客网 时间:2024/05/21 07:50

测试地址:Trade

题目大意:给定连续T天的股市情况,包含四个参数api,bpi,asi,bsi,表示第i天买价为api一股,卖价为bpi一股,当天最多能买asi股,最多能卖bsi股,一天之内只能在买和卖中选择一个,另外限制任何一天手中股票不能超过maxp股,如果在一天进行了交易(买或卖),那么接下来W天都不能交易。一开始有无限的本金,但是没有股票,求最后最多能赚多少钱。

做法:每一天的操作无非就是三种:不交易,买,卖。按照这个写出状态转移方程,设dp[i][j]为第i天结束后手中持有j股的最大收益,则:

dp[i][j]=max(dp[i-1][j],max(dp[x][k]-api*(j-k))(x≤i-w-1,k<j),max(dp[x][k]+bpi*(k-j))(x≤i-w-1,k>j));

初始化:dp[0][0]=0,dp[i][j]=-inf(i≠0且j≠0)。

注意到如果枚举x和k的话,这是一个O(T*maxp)的式子,那么总的复杂度将达到O(T^2*maxp^2),华丽爆炸。我们考虑枚举x的必要性,发现我们每次求dp[i][j]都要考察dp[i-1][j]的情况,也就是说dp[i][j]≥dp[i-1][j],所以在k相等的情况下,dp[x][k]在x取最大值时最大,所以x总是i-w-1,复杂度降低到O(T*maxp^2),仍然不能通过此题。再单独观察买和卖的式子,发现都能化成max(w[k])+c(l≤k≤r)的形式,对于买的式子,其中w[k]=dp[i-w-1][k]+api*k,c=-api*j,l=j-asi,r=j-1,卖的式子同理,我们发现在i和j相同的情况下,w[k]是由k唯一确定的变量(且可以O(1)算出),c是一个常数,且k的取值范围大小始终都是一个常数,这启示我们使用单调队列进行优化,于是复杂度降低到O(T*maxp),完美解决该题。

以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#define inf 2000000000using namespace std;int tt,T,maxp,w,api[2010],bpi[2010],asi[2010],bsi[2010];int dp[2010][2010],h,t,q[2010];int main(){  scanf("%d",&tt);  while(tt--)  {    scanf("%d%d%d",&T,&maxp,&w);for(int i=1;i<=T;i++)  scanf("%d%d%d%d",&api[i],&bpi[i],&asi[i],&bsi[i]);for(int i=0;i<=T;i++)  for(int j=0;j<=maxp;j++)    dp[i][j]=-inf;dp[0][0]=0;for(int i=1;i<=T;i++){  int s=max(0,i-w-1);  h=1,t=0;  for(int j=0;j<=maxp;j++)  {    dp[i][j]=max(dp[i][j],dp[i-1][j]);if (j>0){  while(h<=t&&q[h]<j-asi[i]) h++;  while(h<=t&&dp[s][j-1]+api[i]*(j-1)>=dp[s][q[t]]+api[i]*q[t]) t--;      q[++t]=j-1;  dp[i][j]=max(dp[i][j],dp[s][q[h]]-api[i]*(j-q[h]));}  }  h=1,t=0;  for(int j=maxp;j>=0;j--)  {    if (j<maxp){  while(h<=t&&q[h]>j+bsi[i]) h++;  while(h<=t&&dp[s][j+1]+bpi[i]*(j+1)>=dp[s][q[t]]+bpi[i]*q[t]) t--;      q[++t]=j+1;  dp[i][j]=max(dp[i][j],dp[s][q[h]]+bpi[i]*(q[h]-j));}  }}    int ans=0;for(int i=0;i<=maxp;i++)  ans=max(ans,dp[T][i]);printf("%d\n",ans);  }    return 0;}


0 0