P2569 [SCOI2010] 股票交易
P2569 [SCOI2010] 股票交易
题目描述
最近 \(\text{lxhgww}\) 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。
通过一段时间的观察,\(\text{lxhgww}\) 预测到了未来 \(T\) 天内某只股票的走势,第 \(i\) 天的股票买入价为每股 \(AP_i\),第 \(i\) 天的股票卖出价为每股 \(BP_i\)(数据保证对于每个 \(i\),都有 \(AP_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
输入 #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\%\) 的数据,\(0\leq W<T\leq 2000,1\leq\text{MaxP}\leq50\);
- 对于 \(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}\)。
思路
数据范围一眼dp,这道题很重要的两个点,分别是第几天和持有的股票数,那么直接基于此设计出状态:
设 \(f_{i,j}\) 表示到了第 \(i\) 天,手中持有 \(j\) 股的最大利润,那么转移方程式就很容易得到了。
然后发现这里一共 \(4\) 个循环变量,肯定无法接受,我们需要的是 \(O(n^2)\) 的算法,肯定需要优化。
观察可以发现其实 \(i-k\) 这一维之中 \(k\) 其实没必要,因为在 \(i-w-1\) 这一天中的状态就已经包含了前面所有状态的最优解。
因此可以少一维 \(k\) 枚举。这里再把方程写出来:
做到这一步之后我们应继续优化,看到这种取 \(\max\) 的转移方程式,还要求时间复杂度降一维,肯定要想单调队列优化。
那么考虑单调队列里面存什么?
对于我当时而言,我并没有从方程式里面看出来我应该怎么存,所以说我先把 \(O(n^3)\) 的暴力写了出来,然后通过代码来看怎么存。
for(int i=1;i<=T;i++){
for(int j=0;j<=maxp;j++) f[i][j]=f[i-1][j];
int k=w+1;
if(k>i) k=i;
for(int j=0;j<=maxp;j++){
for(int p=1;p<=bs[i] && j+p<=maxp;p++){
f[i][j]=max(f[i][j],f[i-k][j+p]+p*bp[i]);
}
for(int p=1;p<=as[i] && p<=j;p++){
f[i][j]=max(f[i][j],f[i-k][j-p]-p*ap[i]);
}
}
}
因为单调队列是每次循环只会入队一个元素,那么第一个单调队列显然要在我正式开始转移之前先把其他的元素给它整进去,那么这样我在每次循环的时候就只需要入队 \(f_{i-k,j+bs_i}+bs_i \cdot bp_i\) 就可以了,下面的那个第二个单调队列同理,略微不同的在于它不需要提前入队。
这道题的代码也是值得复习巩固的。
code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e3+100,inf=-4485090715960753727;
int T,maxp,w;
int ap[N],bp[N],as[N],bs[N];
int f[N][N];
deque <int> dq1;
deque <int> dq2;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T>>maxp>>w;
for(int i=1;i<=T;i++) cin>>ap[i]>>bp[i]>>as[i]>>bs[i];
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=T;i++){
for(int j=0;j<=maxp;j++) f[i][j]=f[i-1][j];
int k=w+1;
if(k>i) k=i;
dq1.clear();
for(int p=1;p<bs[i] && p<=maxp;p++){
while(!dq1.empty() && f[i-k][dq1.back()]+dq1.back()*bp[i]<=f[i-k][p]+p*bp[i]) dq1.pop_back();
dq1.push_back(p);
}
for(int j=0;j<=maxp;j++){
int p=j+bs[i];
if(p<=maxp){
while(!dq1.empty() && f[i-k][dq1.back()]+(dq1.back()-j)*bp[i]<=f[i-k][p]+(p-j)*bp[i]) dq1.pop_back();
dq1.push_back(p);
}
while(!dq1.empty() && dq1.front()<j+1) dq1.pop_front();
if(!dq1.empty()) f[i][j]=max(f[i][j],f[i-k][dq1.front()]+(dq1.front()-j)*bp[i]);
}
dq2.clear();
for(int j=0;j<=maxp;j++){
if(j>=1){
while(!dq2.empty() && f[i-k][dq2.back()]-(j-dq2.back())*ap[i]<=f[i-k][j-1]-ap[i]) dq2.pop_back();
dq2.push_back(j-1);
}
while(!dq2.empty() && dq2.front()<j-as[i]) dq2.pop_front();
if(!dq2.empty()) f[i][j]=max(f[i][j],f[i-k][dq2.front()]-(j-dq2.front())*ap[i]);
}
}
cout<<f[T][0];
return 0;
}

浙公网安备 33010602011771号