洛谷P2569 [SCOI2010]股票交易

题目描述

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

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

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

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

输入格式

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

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

输出格式

输出数据为一行,包括 \(1\) 个数字,表示 \(\text{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

说明/提示

对于 \(30\%\) 的数据,\(0\leq W<T\leq 50,1\leq\text{MaxP}\leq50\)

对于 50%50%50% 的数据,\(0\leq W<T\leq 2000,1\leq\text{MaxP}\leq50\)

对于 100%100%100% 的数据,\(0\leq W<T\leq 2000,1\leq\text{MaxP}\leq2000\)

对于所有的数据,\(1\leq BP_i\leq AP_i\leq 1000,1\leq AS_i,BS_i\leq\text{MaxP}\)

Solution

状态:

\(f_{i,j}\) 表示在第 \(i\) 天持有 \(j\) 张股票时可以赚到的最多钱数。

转移:

可以分为4种情况

  1. \(j\) 张股票
    \(f_{i,j}=-1 \times ap_i \times j\)

  2. 不买也不卖
    直接从 \(i-1\) 天转移
    \(f_{i,j}=\max(f_{i,j},f_{i-1,j})\)

  3. 假设已经有 \(k\) 张股票,要买 \(j-k\)
    因为2次交易之间要隔 \(w\) 天,所以从 \(i-w-1\) 天转移
    \(f_{i,j}=\max(f_{i,j},f_{i-w-1,k}-(j-k) \times ap_i)\)

  4. 假设已经有 \(k\) 张股票,要卖 \(k-j\)
    一样是从 \(i-w-1\) 天转移
    \(f_{i,j}=\max(f_{i,j},f_{i-w-1,k}+(k-j) \times bp_i)\)

但这样做是 \(O(N^3)\) 的,因此要想办法去掉一层循环。

以第3种为例

\(f_{i,j}=\max(f_{i,j},f_{i-w-1,k}-(j-k) \times ap_i)\)

其中 \(f_{i-w-1,k}-(j-k) \times ap_i=f_{i-w-1,k}+k \times ap_i-j \times ap_i\)

因此 \(f_{i,j}=\max(f_{i,j},f_{i-w-1,k}+k \times ap_i)-j \times ap_i \qquad (j-as_i \leq k < j)\)

所以就可以用单调队列优化了,只需要维护 \(j-as_i \leq k < j\)\(f_{i-w-1,k}+k \times ap_i\) 的最大值。

第4种同理

\(f_{i,j}=\max(f_{i,j},f_{i-w-1,k}+k \times bp_i)-j \times bp_i \qquad (j<k \leq j+bs_i)\)

维护 \(j<k \leq j+bs_i\)\(f_{i-w-1,k}+k \times bp_i\) 的最大值

但这里要从大到小排序,因为要卖股票 \(k>j\) 是用大的推小的。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
 
using namespace std;

const int N=2010;
int n,m,w;
int f[N][N];
int q[N],h,t;

int main()
{
	scanf("%d%d%d",&n,&m,&w);
	memset(f,128,sizeof(f));
	for(int i=1;i<=n;i++)
	{
		int ap,bp,as,bs;
		scanf("%d%d%d%d",&ap,&bp,&as,&bs);
		for(int j=0;j<=as;j++)
			f[i][j]=-1*ap*j;
		for(int j=0;j<=m;j++)
			f[i][j]=max(f[i][j],f[i-1][j]);
		if(i<=w) continue;
		h=1,t=0;
		for(int j=0;j<=m;j++)
		{
			while(h<=t&&q[h]<j-as) h++;
			while(h<=t&&f[i-w-1][j]+ap*j>=f[i-w-1][q[t]]+ap*q[t]) t--;
			q[++t]=j;
			f[i][j]=max(f[i][j],f[i-w-1][q[h]]+ap*q[h]-ap*j);
		}
		h=1,t=0;
		for(int j=m;j>=0;j--)
		{
			while(h<=t&&q[h]>j+bs) h++;
			while(h<=t&&f[i-w-1][j]+bp*j>=f[i-w-1][q[t]]+bp*q[t]) t--;
			q[++t]=j;
			f[i][j]=max(f[i][j],f[i-w-1][q[h]]+bp*q[h]-bp*j);
		}
	}
	int ans=0;
	for(int i=0;i<=m;i++)
		ans=max(ans,f[n][i]);
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-04-04 23:37  Acestar  阅读(71)  评论(0编辑  收藏  举报