zoukankan      html  css  js  c++  java
  • 【题解】[SCOI2010]股票交易

    Link

    ( ext{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]=maxlimits_{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
    ",ans);
    	return 0;
    } 
    
  • 相关阅读:
    排序
    FileOutputStream文件写入 —— 覆盖、追加
    判断一个地图坐标是否在中国镜内
    地图坐标转换
    全文检索 使用最新lucene3.0.3+最新盘古分词 pangu2.4 .net 实例
    blob转base64位 base64位转blob
    div中粘贴图片并上传服务器 div中拖拽图片文件并上传服务器
    DIV 粘贴插入文本或者其他元素后,移动光标到最新处
    project 计划添加编号或 任务分解时为任务添加编号
    修改project任务的默认开始时间
  • 原文地址:https://www.cnblogs.com/h-lka/p/13475914.html
Copyright © 2011-2022 走看看