1 5 2 0 2 1 1 1 2 1 1 1 3 2 1 1 4 3 1 1 5 4 1 1
3
有N天,每天你最多能拥有的股票数是MaxP,两次操作间隔必须大于W天(买和卖都算操作)。接下来N行给出每天买一股的价格APi,卖一股的价格BPi,每天最多买ASi股,最多卖BSi股,问最大收入是多少,一开始资金无限。
用dp[i][j]表示第i天拥有股票j的最大收入,以第j天买股票为例,那么dp[i][j]=max(dp[i][j],dp[i-W-1][k]+(j-k)*ap[i]) (j-k<=as[i]),对于每个j遍历k会超时。不难发现只要在j之前找一个k使dp[i-W-1][k]-k*ap[i](设为value)最大并且j-k>=as[i],那么只需要在j从0增大的过程中维护一个value递减序列,对于每个j从当前队列front开始找到第一个满足购买数量限制的,从而得到dp[i][j]。
卖和买原理是一样的。注意初始化。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<vector> #include<cmath> #include<queue> #include<stack> #include<map> #include<set> #include<algorithm> using namespace std; typedef long long LL; const int MAXN=2010; const int MAXM=20010; const LL MOD=1e9+7; const int INF=0x3f3f3f3f; int T; int N,P,W; int ap[MAXN],bp[MAXN],as[MAXN],bs[MAXN]; int dp[MAXN][MAXN]; struct Stock{ int p,value; }q[MAXN]; int main(){ freopen("in.txt","r",stdin); scanf("%d",&T); while(T--){ scanf("%d%d%d",&N,&P,&W); for(int i=1;i<=N;i++) scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]); for(int i=0;i<=N;i++) for(int j=0;j<=P;j++) dp[i][j]=-INF; dp[0][0]=0; for(int i=1;i<=N;i++){ for(int j=0;j<=P;j++) dp[i][j]=max(dp[i][j],dp[i-1][j]); if(i-W-1<=0){ for(int j=0;j<=as[i];j++) dp[i][j]=max(dp[i][j],-ap[i]*j); } else{ //买 int front=0,rear=-1; for(int j=0;j<=P;j++){ Stock tmp=(Stock){j,dp[i-W-1][j]+j*ap[i]}; while(front<=rear&&q[rear].value<=tmp.value) rear--; q[++rear]=tmp; while(front<=rear&&q[front].p+as[i]<j) front++; dp[i][j]=max(dp[i][j],q[front].value-ap[i]*j); } //卖 front=0; rear=-1; for(int j=P;j>=0;j--){ Stock tmp=(Stock){j,dp[i-W-1][j]+j*bp[i]}; while(front<=rear&&q[rear].value<=tmp.value) rear--; q[++rear]=tmp; while(front<=rear&&q[front].p-bs[i]>j) front++; dp[i][j]=max(dp[i][j],q[front].value-bp[i]*j); } } } int ans=0; for(int i=0;i<=P;i++) ans=max(ans,dp[N][i]); printf("%d\n",ans); } return 0; }