#单调队列,动态规划#codevs 2417 codevs 3359 洛谷 2569 jzoj 1593 股票交易

本文介绍了一种基于动态规划和单调队列的股票交易策略算法,该算法能够在限制交易频率和持有股票数量的情况下最大化收益。通过调整状态转移方程,算法有效地解决了时间复杂度过高的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

iii天的股票买入价为每股APiAPiAPi,第i天的股票卖出价为每股BPiBPiBPi(数据保证对于每个iii,都有APi≥BPiAPi\geq BPiAPiBPi),规定第iii天的一次买入至多只能购买ASiASiASi股,一次卖出至多只能卖出BSiBSiBSi股。两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔WWW天,也就是说如果在第iii天发生了交易,那么从第i+1i+1i+1天到第i+Wi+Wi+W天,均不能发生交易。一个人的手里的股票数不能超过MaxPMaxPMaxPTTT天以后,问最多获利多少钱


分析

f[i][j]f[i][j]f[i][j]表示第iii天结束后,手里剩下jjj股的最大利润

  • 不买不卖:f[i][j]=f[i−1][j]f[i][j]=f[i-1][j]f[i][j]=f[i1][j]
  • 买入:
    f[i][j]=max⁡(f[i−w−1][k]−ap[i]∗(j−k))f[i][j]=\max(f[i-w-1][k]-ap[i]*(j-k))f[i][j]=max(f[iw1][k]ap[i](jk))
    (j−as[i]≤k&lt;j)(j-as[i]\leq k&lt;j)(jas[i]k<j)
  • 卖出:
    f[i][j]=max⁡(f[i−w−1][k]+bp[i]∗(k−j))f[i][j]=\max(f[i-w-1][k]+bp[i]*(k-j))f[i][j]=max(f[iw1][k]+bp[i](kj))
    (j&lt;k≤j+bs[i])(j&lt;k\leq j+bs[i])(j<kj+bs[i])

但是问题是O(TW2)O(TW^2)O(TW2)会超时,那么改一改

  • 买入:
    f[i][j]=max⁡(f[i−w−1][k]+ap[i]∗k)−ap[i]∗jf[i][j]=\max(f[i-w-1][k]+ap[i]*k)-ap[i]*jf[i][j]=max(f[iw1][k]+ap[i]k)ap[i]j
  • 卖出:
    f[i][j]=max⁡(f[i−w−1][k]+bp[i]∗k)−bp[i]∗jf[i][j]=\max(f[i-w-1][k]+bp[i]*k)-bp[i]*jf[i][j]=max(f[iw1][k]+bp[i]k)bp[i]j

那么就可以用单调队列来解决,求出最优的k


代码

#include <cstdio>
#include <cstring>
using namespace std;
int n,maxp,w,ap[2001],bp[2001],as[2001],bs[2001],f[2001][2001],q[2001];
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
int max(int a,int b){return (a<b)?b:a;}
int main(){
	n=in(); maxp=in(); w=in(); memset(f,-0x7f,sizeof(f));
	for (int i=1;i<=n;i++) ap[i]=in(),bp[i]=in(),as[i]=in(),bs[i]=in();
	for (int i=0;i<=n;i++) f[i][0]=0;//没有股票为0
	for (int i=1;i<=n;i++){
		for (int j=0;j<=as[i];j++) f[i][j]=-ap[i]*j;//买了该股票花钱
		for (int j=maxp;j>=0;j--) f[i][j]=max(f[i][j],f[i-1][j]);//不买股票
		if (i>w){//可以交易
			int head=1,tail=0;
			for (int j=0;j<=maxp;j++){
			    while (head<=tail&&q[head]<j-as[i]) head++;//股票数量不够
			    while (head<=tail&&f[i-w-1][j]+ap[i]*j>=f[i-w-1][q[tail]]+ap[i]*q[tail]) tail--;//更优的答案
		        q[++tail]=j;
		        if (head<=tail) f[i][j]=max(f[i][j],f[i-w-1][q[head]]-ap[i]*(j-q[head]));//dp
			}
			head=1; tail=0;
			for (int j=maxp;j>=0;j--){
				while (head<=tail&&q[head]>j+bs[i]) head++;//股票不够
				while (head<=tail&&f[i-w-1][j]+bp[i]*j>=f[i-w-1][q[tail]]+bp[i]*q[tail]) tail--;//更优的答案
				q[++tail]=j;
				if (head<=tail) f[i][j]=max(f[i][j],f[i-w-1][q[head]]+bp[i]*(q[head]-j));//dp
			}
		}
	}
	int ans=0;
	for (int i=0;i<=maxp;i++) ans=max(ans,f[n][i]);//找到最好的答案
	return !printf("%d",ans);
}

PS:SSL 1943 vijos new_bzoj

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值