【HDU3401】Trade——【Luogu_P2569】股票交易(有改动)

单调队列 同时被 3 个专栏收录
9 篇文章 0 订阅
51 篇文章 0 订阅
183 篇文章 0 订阅

题目描述

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

通过一段时间的观察,\text{lxhgww}lxhgww 预测到了未来 TT 天内某只股票的走势,第 ii 天的股票买入价为每股 AP_i,第 ii 天的股票卖出价为每股 BP_i ,但是每天不能无限制地交易,于是股票交易所规定第 i 天的一次买入至多只能购买 AS_i 股,一次卖出至多只能卖出 BS i股。

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

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

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

接下来 T 行,第 i 行代表第 i−1 天的股票走势,每行 4 个整数,分别表示 AP_i,\ BP_i,\ AS_i,\ BS_iAP i


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

输入输出样例

输入 #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

思路:
设置两次单调队列,一次求卖出的,一次求买入的,这样就能避免卖出买入纵横交错,杂乱无章,毫无头绪
因为 W + 1 W+1 W+1天才能进行一次交易,所以我们只能用 i − w − 1 i-w-1 iw1作为前驱
那动态转移方程为:买入:f[i][j]=max(q[k][1]-jxa[i])
卖出:f[i][j]=max(q[k][1]-jxb[i])
再用单调队列维护就行

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
int t, f[2001][2001], q[100010][2], a[2001], b[2001], as[2001], bs[2001];
int main()
{
	scanf("%d", &t);
	while(t--)
	{
		int w, t, maxp;
		scanf("%d%d%d", &t, &maxp, &w);
		for(int i=0; i<=2000; i++)
			for(int j=0; j<=2000; j++)
				f[i][j]=-2147483647;//初始化
		for(int i=1; i<=t; i++)
			scanf("%d%d%d%d", &a[i], &b[i], &as[i], &bs[i]);
		for(int i=1; i<=w+1; i++)
			for(int j=0; j<=as[i]&&j<=maxp; j++)
				f[i][j]=-j*a[i];//因为1-w+1这几天没有可用的前驱,所以直接让他们成为w+1-t的前驱
		for(int i=1; i<=t; i++)
		{
			for(int j=0; j<=maxp; j++)
				f[i][j]=max(f[i][j], f[i-1][j]);//假设这一天什么都不做
			int head=1, tail=0;
			if(i<=w+1)continue;
			int s=i-w-1;
			for(int j=0; j<=maxp; j++)
			{
				int sum=f[s][j]+j*a[i];
				while(head<=tail&&sum>q[tail][1])tail--;
				q[++tail][0]=j;
				q[tail][1]=sum;
				while(head<=tail&&j-q[head][0]>as[i])head++;
				f[i][j]=max(f[i][j], q[head][1]-j*a[i]);//买入
			}
			head=1, tail=0;
			for(int j=maxp; j>=0; j--)
			{
				int sum=f[s][j]+j*b[i];
				while(head<=tail&&sum>q[tail][1])tail--;
				q[++tail][0]=j;
				q[tail][1]=sum;
				while(head<=tail&&q[head][0]-j>bs[i])head++;
				f[i][j]=max(f[i][j], q[head][1]-j*b[i]);//卖出
			}
		}
		int ans=0;
		for(int j=0; j<=maxp; j++)
			ans=max(ans, f[t][j]);
		printf("%d\n", ans); 
	}
	return 0;
}
  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值