测试地址: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 2000000000 using 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; }