【洛谷 P2569&HDU 3401】[SCOI 2010]股票交易【单调队列优化のDP】

题目描述

最近 lxhgww 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。

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

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

在第 1 天之前,lxhgww 手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T 天以后,lxhgww 想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?

输入格式

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

接下来 T行,第 i行代表第 i−1 天的股票走势,每行 4 个整数,分别表示 AP i , BP i , AS i , BS i​ 。

输出格式

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

输入输出样例

输入 #1

5 2 0
2 1 1 1
2 1 1 1
3 2 1 1
4 3 1 1
5 4 1 1

输出 #1

3

分析:

这道题显然用DP f[i][j]表示i天拥有j张股票赚的钱
共分④种情况
1 .买空
可直接赋值

f[i][j]=-1*ap[i]*j

2 .不买也不卖
直接由上一天转移 并且股票数不变

f[i][j]=max(f[i][j],f[i-1][j]);

3 .之前状态下买股票
由于w天内不能交易 所以最近的交易时间为 i - w - 1
x 一定比 j 小 因为最多买AS张 所以底线是 j - as[i]
假设i - w - 1天x张股票 当前买了 j - x交易额为 ( j - x) * ap[i]
整理一下 得到方程

f[i][j]=max(f[i][j],f[i-w-1][x-(j-x)*ap[i]])

4 .之前状态下卖股票
情况3差不多 有几点区别
x 一定比 j 大 底线为 j + bs[i]
卖了 j - x交易额为 ( j - x) * bp[i]
与情况3的方程基本一致 把 ap改bp as改bs 就行
符合单调队列的性质 要用单调队列优化
买股票是顺序 卖股票是逆序 其他就没啥了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;
int n,maxp,w,as,ap,bs,bp,q[2005],f[2005][2005],ans=0,head,tail;
int main(){
	scanf("%d%d%d",&n,&maxp,&w);
	memset(f,128,sizeof(f));
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&ap,&bp,&as,&bs);
		for(int j=0;j<=as;j++)
			f[i][j]=-1*j*ap;  //情况1
		for(int j=0;j<=maxp;j++)
			f[i][j]=max(f[i][j],f[i-1][j]);  //情况2
		if(i<=w) continue;  //如果i<=w会出现负下标
		head=1,tail=0;  //单调队列
		for(int j=0;j<=maxp;j++){
			while(head<=tail&&q[head]<j-as) head++;
			while(head<=tail&&f[i-w-1][q[tail]]+q[tail]*ap<=f[i-w-1][j]+j*ap) tail--;  //单调队列优化
			//情况3 顺序
			q[++tail]=j;  //入队
			if(head<=tail) 
			f[i][j]=max(f[i][j],f[i-w-1][q[head]]+q[head]*ap-j*ap);  //DP
		}
		head=1;tail=0;
		for(int j=maxp;j>=0;j--){ //情况4 逆序
			while(head<=tail&&q[head]>j+bs) head++;
			while(head<=tail&&f[i-w-1][q[tail]]+q[tail]*bp<=f[i-w-1][j]+j*bp) tail--;  //单调队列优化
			q[++tail]=j;  //入队
			if(head<=tail) 
			f[i][j]=max(f[i][j],f[i-w-1][q[head]]+q[head]*bp-j*bp);  //DP
		} 
	}
	for(int i=0;i<=maxp;i++)
		ans=max(ans,f[n][i]);  //取最多
	printf("%d",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值