[SCOI2010]股票交易

洛谷题目连接:[SCOI2010]股票交易

题目描述

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

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

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

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

输入输出格式

输入格式:

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

接下来T行,第i行代表第i-1天的股票走势,每行4个整数,分别表示APi,BPi,ASi,BSi。

输出格式:

输出数据为一行,包括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

说明

对于30%的数据,0<=W<T<=50,1<=MaxP<=50

对于50%的数据,0<=W<T<=2000,1<=MaxP<=50

对于100%的数据,0<=W<T<=2000,1<=MaxP<=2000

对于所有的数据,1<=BPi<=APi<=1000,1<=ASi,BSi<=MaxP

一句话题意: 每天最多可以买进(as_i)张股票,卖出(bs_i)张股票,但是每个时刻手中的票最多都只能有(maxp)张,每买入一张股票可以获得(ap_i)元,卖出一张股票可以获得(bp_i)元,且如果第(i)天进行了交易,那么从第(i+1)到第(i+w)天都不能再进行交易,问到第(n)天最多可以获得的价值.

题解: 这个数据范围显然是能DP的.我们先想一下状态该如何定义.

首先根据这个状态转移的条件,有交易的天数限制,所以显然是需要一维来存交易到第几天的.然后是对于手中持有的股票数量的限制,显然至少是需要一重循环来枚举目前手中持有的股票数量的,所以考虑将这个也加入状态中.可得状态(f[i][j])表示到第(i)天手中持有(j)张股票的最大利益.那么最后的答案就是
(f[n][0]),因为显然在最后一天把所有股票都卖出不会比留着股票差.

然后想一下如何转移这个状态.那么从上一个状态到现在的状态(f[i][j]),显然只有这几种情况:

  • (i-1)天没有买/卖股票,第(i)天的最大收益为(f[i-1][j]).
  • (i-w-1)天进行了股票的买卖且拥有(k(k<j))张股票,第(i)天有股票(j)张,此时买入了(j-k)张股票,可得最大收益为(f[i][j] = max(f[i][j], f[i-w-1][k]-(j-k)*ap[i]))
  • (i-w-1)天进行了股票的买卖且拥有(k(k>j))张股票,第(i)天有股票(j)张,此时卖出了(k-j)张股票,可得最大收益为(f[i][j]=max(f[i][j], f[i-w-1][k]+(k-j)*bp[i]))

此外没有别的转移方式了,所以可以列出状态转移方程.

那么考虑了状态转移之后,仔细想想发现这个时间复杂度是(O(T*maxp^2))的,显然这样是不能过100%的数据的.所以需要使用一些优化.

这里我们将这个状态转移方程展开一下,发现:

[f[i-w-1][k]+(k-j)×bp[i]=(f[i-w-1][k]+k×bp[i])-j×bp[i] ]

也就是说,在枚举了(i,j)的情况下,(i,j)是可以看做常数的,那么此时对答案有影响的就只有(k) .并且我们发现, 如果(j)是以正确的顺序枚举的,那么(k)也就是逐渐在平移的了.比如说买入股票,那么拥有的股票也就会越来越多,卖出的话拥有的股票就会越来越少,事实上这个是符合 单调队列优化的条件的,所以我们可以考虑将目前拥有的股票数存入单调队列中,优化后复杂度为(O(T*maxp)).

当然分开和卖出的两种情况是要分开使用队列的,因为他们各自具有单调性,但是合起来并没有.

#include<bits/stdc++.h>
using namespace std;
const int N=2000+5;

int n, maxp, w, ap[N], bp[N], as[N], bs[N], ans = 0;
int f[N][N], h1, h2, t1, t2, q1[N], q2[N];

int main(){
    cin >> n >> maxp >> w;
    for(int i=1;i<=n;i++)
        cin >> ap[i] >> bp[i] >> as[i] >> bs[i];
    
    memset(f, 128, sizeof(f)); f[0][0] = 0;
    
	for(int i=1;i<=n;i++){
		for(int j=0;j<=maxp;j++)
			f[i][j] = max(f[i][j], f[i-1][j]);
		for(int j=0;j<=as[i];j++)
			f[i][j] = max(f[i][j], -1*j*ap[i]);
		if(i <= w) continue;
		h1 = h2 = 1, t1 = t2 = 0;
		for(int j=0;j<=maxp;j++){
			while(h1 <= t1 && f[i-w-1][q1[t1]]-ap[i]*(j-q1[t1]) <= f[i-w-1][j]) t1--;
			while(h1 <= t1 && j-q1[h1] > as[i]) h1++;
			q1[++t1] = j;
			if(h1 <= t1) f[i][j] = max(f[i][j], f[i-w-1][q1[h1]]-(j-q1[h1])*ap[i]);
	    }
    	h1 = h2 = 1, t1 = t2 = 0;
	    for(int j=maxp;j>=0;j--){
			while(h2 <= t2 && f[i-w-1][q2[t2]]+bp[i]*(q2[t2]-j) <= f[i-w-1][j]) t2--;
			while(h2 <= t2 && q2[h2]-j > bs[i]) h2++;
			q2[++t2] = j;
			if(h2 <= t2) f[i][j] = max(f[i][j], f[i-w-1][q2[h2]]+(q2[h2]-j)*bp[i]);
	    }
    }
    printf("%d
", f[n][0]);
    return 0;
}
原文地址:https://www.cnblogs.com/BCOI/p/9133358.html