【题解】[SCOI2010]股票交易

Link

\(\text{Solution:}\)

蒟蒻做完这题深知不写清楚\(dp\)转移方程的痛……

首先,显然我们可以设\(dp[i][j]\)表示第\(i\)天,拥有\(j\)股股票的最优解。于是,对于每一天,我们可以:

  • 不进行交易

于是直接\(dp[i][j]=\max(dp[i-1][j],dp[i][j])\)即可。

  • 只买入股票

直接枚举\(j,dp[i][j]=-ap_i*j\)即可。

  • 从之前某一个状态转移过来,并且买股票

首先证明为什么只需要从\(dp[i-w-1][*]\)转移即可。

证明:观察到,每一个状态都记录的是它的最优解。也就说\(dp[i-w-1][*]\)已经把之前的什么\(dp[i-w-...][*]\)的状态转移过了。于是就不必要再去枚举前面那些转移了。

写出转移方程:设我们之前有\(k\)股,买入了\((j-k)\)股。

\[dp[i][j]=\max\limits_{k=0}^{j-c_i} dp[i-w-1][k]-ap_i*(j-k) \]

显然有我们买的越少花钱越少,单调性显然。单调队列里面存\(k.\)注意边界即可。

  • 从之前某一个状态转移过来,并且卖股票

同理,先写出方程:

\[dp[i][j]=\max \limits_{k=j+1}^{j+bs_i} dp[i-w-1][k]+bp*(k-j) \]

显然卖的越多拿钱越多。单调性显然。

那么对于上面所说两种:买和卖,我们的\(dp\)顺序有区别。

对于买:我们显然由股票少的买到股票多的。于是,我们应该从小到大来\(dp\)股票。

对于卖:我们显然由股票多的卖到股票少的。于是,我们应该从大到小来\(dp\)股票。

所以,上述两种单调队列中所贮存的\(k\)的单调性是不一样的。

最后,弹出队尾的话直接拿\(dp\)方程算结果,取更优者即可。

至于为什么要先加入元素再\(dp:\)

考虑到,在这一刻我们是可以不动自己的股票的。而如果这一刻任何买卖都亏本的话,不如在这里就直接把那些亏本状态舍掉,并把\(j\)加入。当然最后会执行一次转移,但为了保证对头的最优解,我们应当先执行弹出的过程。

#include<bits/stdc++.h>
using namespace std;
int T,w,P,as,bs,ap,bp;
int dp[4000][4000];
int q[4000],head,tail;
int main(){
	scanf("%d%d%d",&T,&P,&w);
	memset(dp,128,sizeof(dp));
	for(int i=1;i<=T;++i){
		scanf("%d%d%d%d",&ap,&bp,&as,&bs);
		for(int j=0;j<=as;++j)dp[i][j]=-ap*j;
		//只买入 
		for(int j=0;j<=P;++j)dp[i][j]=max(dp[i][j],dp[i-1][j]);
		//不做交易 
		head=1,tail=0;
		if(i<=w)continue;//不能交易 
		for(int j=0;j<=P;++j){
			while(head<=tail&&q[head]<j-as)head++;//这里的单调队列递增 
			while(head<=tail&&dp[i-w-1][q[tail]]-(j-q[tail])*ap<=dp[i-w-1][j])tail--;//如果队尾答案不优秀就一直更新 
			q[++tail]=j;//元素入队 
			if(head<=tail)dp[i][j]=max(dp[i][j],dp[i-w-1][q[head]]-(j-q[head])*ap);//若里面还有元素就更新 
		}
		head=1,tail=0;
		for(int j=P;j>=0;--j){
			while(head<=tail&&q[head]>j+bs)head++;//这里的单调队列递减 
			while(head<=tail&&dp[i-w-1][q[tail]]+(q[tail]-j)*bp<=dp[i-w-1][j])tail--;
			q[++tail]=j;
			if(head<=tail)dp[i][j]=max(dp[i][j],dp[i-w-1][q[head]]+(q[head]-j)*bp);
		}
	}
	int ans=-1;
	for(int i=0;i<=P;++i)ans=max(ans,dp[T][i]);
	printf("%d\n",ans);
	return 0;
} 
posted @ 2020-08-11 15:31  Refined_heart  阅读(140)  评论(0编辑  收藏  举报