[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
现在单调队列都快忘了==
写的详细点复习一下
首先有一个肥肠显然的70pts的做法
就是设f[i][j]表示第i天结束后还有j股
然后分4种情况讨论一下
sit1 : 啥也不干
f[i][j] = max(f[i][j] , f[i - 1][j])
sit2 :i <= w + 1 天时买股票
f[i][j] = - Ap[i] * j
sit3 : 在 i > w + 1 天时买股票
这时我们就从第 i - w - 1 天转移即可(因为sit1已经把前面的结果压到第i - w - 1天了)
然后因为有买入上界的限制,所以 max(j-As[i]) <= k <= j
f[i][j] = max(f[i][j] , f[i - w - 1][k] - (j - k) * Ap[i])
sit4 : 在 i > w + 1 天时抛股票
和sit3同理
j <= k <= min(Maxp , j + Bs[i])
f[i][j] = max(f[i][j] , f[i - w - 1][k] + (k - j) * Bp[i])
这样就能得到一个n^3的暴力DP
问题主要出在sit3和sit4
我们考虑对sit3和sit4优化
化简一下sit3和sit4的式子
sit3 : f[i][j] = max(f[i][j] , f[i - w - 1][k] + k * Ap[i] - j * Ap[i])
sit4 :f[i][j] = max(f[i][j] , f[i - w - 1][k] + k * Bp[i] - j * Bp[i])
发现可以用单调队列维护使f[i - w - 1][k] + k * Ap[i]在合法的区间内能取得最大值的k
还有就是要注意循环的顺序
sit3要正着循环
因为sit3的每个合法区间 j - As[i] ~ j 是 <=j 的
所以新进来的元素顶掉的合法区间外的元素一定在后来进来的元素的合法区间之外
sit4要倒着循环
因为sit4的每个合法区间 j ~ j + Bs[i] 是 >= j 的
与sit3同理
这样复杂度就成n^2辣
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 2005 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x * 10+c-'0' ;c = getchar() ; }
return x*w ;
}
int n , Maxp , w ;
int Ap[M] , Bp[M] , As[M] , Bs[M] ;
int f[M][M] ;
int q[M] , head , tail ;
int Ans ;
int main() {
n = read() ; Maxp = read() ; w = read() ;
for(int i = 1 ; i <= n ; i ++) {
Ap[i] = read() ; Bp[i] = read() ;
As[i] = read() ; Bs[i] = read() ;
}
memset(f , -63 , sizeof(f)) ;
for(int i = 1 ; i <= n ; i ++) {
for(int j = 1 ; j <= As[i] ; j ++) f[i][j] = - j * Ap[i] ;
for(int j = 0 ; j <= Maxp ; j ++)
f[i][j] = max(f[i][j] , f[i - 1][j]) ;
if(i <= w + 1) continue ;
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][q[tail]] + q[tail] * Ap[i] <= f[i - w - 1][j] + j * Ap[i])
tail-- ;
q[++tail] = j ;
if(head <= tail)
f[i][j] = max(f[i][j] , f[i - w - 1][q[head]] + q[head] * Ap[i] - j * Ap[i]) ;
}
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][q[tail]] + q[tail] * Bp[i] <= f[i - w - 1][j] + j * Bp[i])
tail -- ;
q[++tail] = j ;
if(head <= tail)
f[i][j] = max(f[i][j] , f[i - w - 1][q[head]] + q[head] * Bp[i] - j * Bp[i]) ;
}
}
for(int i = 0 ; i <= Maxp ; i ++) Ans = max(Ans , f[n][i]) ;
printf("%d\n",Ans) ;
return 0 ;
}