【题解】[SCOI2010]股票交易

十分普通的DP+不平凡的转移

传送门

这道题状态十分明显。转移是(O(n^4))的,过不去,我们需要优化。

一个十分显然的DP是(f(i,j))表示第(i)天时候拥有(j)单位股票的最大收益。(可以小于零)。它的转移方式是:

(f(i,j)=max(f(k,b)+(b-j) imes sell[t] or(buy[t])))

此时,我们发现一个转移需要两重循环,也就是(O(n^2)),加上右边的式子,就是(O(n^4))的。

考虑优化:

  • 对于转移方程,发现我们可以进行参变量分离。对状态转移方程进行代数变形。

    • (RHS=max(f(k,b)+b imes sell[t]-j imes sell[t]))
    • (j)在转移时,是最开始那个等式的(LHS),可以提前确定,从(max)拿出来。
    • (RHS=max(f(k,b)+b imes sell[t])-j imes sell[t])
    • 于是,对于(max(f(k,b)+b imes sell[t])),可以拿数据结构维护了。
  • 对于转移方法,我们只需要(f(i-W-1,j) , jle maxP)转移过来就好了。这一定是最优的那个状态,比它要早的状态一定会$le f(i-W-1,j) $。

    • 因为这个(晚的)状态在转移的时候,考虑过 从比他 (早的) 状态直接转移过来,于是(f(i-W-1,j))不小于前面那个。

于是,我们可以将(f(i-W-1,b) ,ble maxP)的所有状态压入某个数据结构。我们发现,对于这种数据结构的要求是:

  • 维护最大值
  • 可以清理过期状态(买入卖出股票有多少限制)
  • 重置方便

就是单调队列吃饭的本领。

一句话解释单调队列:一个状态比你小(限制条件少)还比你强(数值大),那么你就被淘汰了。

单调队列是(O(n))的,枚举(LHS)(i)(O(n))的。总复杂度(O(n^2)),可以过了。


#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<bitset>
#include<vector>
#include<map>
#include<ctime>
#include<cstdlib>
#include<set>
#include<bitset>
#include<stack>
#include<list>
#include<cmath>
using namespace std;
#define RP(t,a,b) for(register ll (t)=(a),edd_=(b);t<=edd_;++t)
#define DRP(t,a,b) for(register ll (t)=(a),edd_=(b);t>=edd_;--t)
#define ERP(t,a) for(int t=head[a];t;t=e[t].to)
#define Min(a,b) ((a)<(b)?(a):(b))
#define TMP template<class ccf>
typedef long long ll;
TMP inline ccf qr(ccf k){
   char c=getchar();
   ccf x=0;
   int q=1;
   while(c<48||c>57)
   q=c==45?-1:q,c=getchar();
   while(c>=48&&c<=57)
   x=x*10+c-48,c=getchar();
   if(q==-1)
   x=-x;
   return x;
}
const int maxn=2e3+15;
inline void Swap(ll& a,ll& b){
   ll k=a;
   a=b;
   b=k;
}

ll buy[maxn];
ll sell[maxn];
ll buytimes[maxn];
ll selltimes[maxn];
ll n,maxP,W;
ll dp[maxn][maxn];
int q[maxn];

TMP inline ccf Max(ccf a,ccf b){
   if(a<b)
   return b;
   return a;
}



inline void init(){
   memset(dp,-0xf,sizeof dp);
   RP(t,1,n){
   buy[t]=qr(1ll);
   sell[t]=qr(1ll);
   buytimes[t]=qr(1ll);
   selltimes[t]=qr(1ll);
   }
   RP(t,1,n){
   int head=1,tail=0;
   if(t-W-1>0){
       
       //trade
       DRP(i,maxP,0){
   	while(head<=tail&&q[head]>i+selltimes[t])
   	    head++;
   	while(head<=tail&&
   	      dp[t-W-1][q[tail]]+1ll*q[tail]*sell[t]
   	      <=dp[t-W-1][i]+1ll*i*sell[t]
   	      )
   	    --tail;
   	q[++tail]=i;
   	if(head<=tail)
   	    dp[t][i]=max(dp[t][i],dp[t-W-1][q[head]]+(q[head]-i)*sell[t]*1ll);
       }

       //purchase
       head=1,tail=0;
       RP(i,0,maxP){
   	while(head<=tail&&q[head]<i-buytimes[t])
   	    ++head;
   	while(head<=tail&&
   	      dp[t-W-1][q[tail]]+q[tail]*buy[t]
   	      <=dp[t-W-1][i]+i*buy[t])
   	    --tail;
   	q[++tail]=i;
   	if(head<=tail)
   	    dp[t][i]=Max(dp[t][i],dp[t-W-1][q[head]]+(q[head]-i)*buy[t]*1ll);
       }
   }

   //restart
   RP(i,0,maxP){
       dp[t][i]=Max(dp[t][i],dp[t-1][i]);
       if(i<=buytimes[t])
   	dp[t][i]=Max(dp[t][i],-i*buy[t]);
   }
   
   }
   ll ans=0;
   RP(t,0,maxP)
   ans=max(ans,dp[n][t]);
   cout<<ans<<endl;
   
}



int main(){
#ifndef ONLINE_JUDGE
   freopen("in.in","r",stdin);
   freopen("out.out","w",stdout);
#endif
   while(~scanf("%lld%lld%lld",&n,&maxP,&W))
   init();
   return 0;
}



原文地址:https://www.cnblogs.com/winlere/p/10307970.html