股票交易(单调队列优化DP)

股票交易:

通过一段时间的观察,预测到了未来T天内某只股票的走势,第i天的股票买入价为每股 (AP_i)​第i天的股票卖出价为每股(BP_i)(数据保证对于每个i,都有(AP_i geq BP_i)),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买(AS_i)股,一次卖出至多只能卖出(BS_i)股.

另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过( ext{MaxP}).

在第 1天之前,手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T天以后,还是想要赚到最多的钱.

输入数据第一行包括 3个整数,分别是 T,( ext{MaxP}),W。

接下来T行,第i行代表第i-1天的股票走势,每行4个整数,分别表示(AP_i, BP_i, AS_i, BS_i).

输出数据为一行,包括 1个数字,表示能赚到的最多的钱数。

t=read();maxp=read();w=read();
//时间t天,一个人最多拥有股票不超过maxp
//两次交易(买和卖都分别算一次)之间至少间隔w天
memset(f,128,sizeof(f));
//f[i][j]表示第i天,有j只股票时的最大收益值
for(int i=1,ap,bp,as,bs;i<=t;i++){
	ap=read();bp=read();
//今天该股票的买入价ap,卖出价bp
	as=read();bs=read();
//今天最多买入as股,卖出bs股
	for(int j=0;j<=as;j++)
	    f[i][j]=-1*j*ap;
//第一种情况:只买入股票
	for(int j=0;j<=maxp;j++)
	    f[i][j]=max(f[i][j],f[i-1][j]);
//第二种情况:不买也不卖,直接由昨天继承而来
	if(i-w-1<=0)continue;
//如果今天还不能发生交易,则跳过对下面两种情况的讨论
	int l=1,r=0;
	for(int j=0;j<=maxp;j++){
	    while(l<=r&&q[l]<j-as)l++;
//把过期元素扔掉:
//q[l]表示第l天拥有股票的数量,
//如果其加上今天最多买入的股票数量as仍达不到j只股票
//那么这个元素就是过期的
	    if(l<=r)f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*ap-j*ap);
//第i天由第i-w-1天而来,假设第i-w-1天有q[l]只股票
//则实际上我们买进了(q[l]-j)只股票,每只股票ap元
	    while(l<=r&&f[i-w-1][q[r]]+q[r]*ap<=f[i-w-1][j]+j*ap)r--;
//单调队列优化
	    q[++r]=j;//入队
	}
//第三种情况:在之前的基础上买入股票
	l=1;r=0;
	for(int j=maxp;j>=0;j--){
	    while(l<=r&&q[l]>j+bs)l++;
	    if(l<=r)f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*bp-j*bp);
	    while(l<=r&&f[i-w-1][q[r]]+q[r]*bp<=f[i-w-1][j]+j*bp)r--;
	    q[++r]=j;
	}
//第四种情况:在之前的基础上卖出股票
}
printf("%d
",f[t][0]);
//第t天时,当然是手上没有股票了收益最大
原文地址:https://www.cnblogs.com/PPXppx/p/10322007.html