题目
最近 \(\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}\) 想要赚到最多的钱, 聪明的程序员们, 你们能帮助他吗?
分析
设 \(f(i,j)\) 代表第 \(i\) 持有 \(j\) 股的最大收益.
\[ f(i,j) = \max\left\{ \begin{array}{} \max_{k = 1}^{as_i} f(i - w - 1,j - k) - ap_i\times k \\max_{k = 1}^{bs_i} f(i - w - 1,j + k) + bp_i\times k \f(i - 1,j) \end{array} \right. \]
很容易得出, 该方程的时间复杂度为 \(\Theta (n^3)\). 显然会 \(\rm T\).
以只买入的 \(\max_{k = 1}^{as_i} f(i - w - 1,j - k) - ap_i\times k\) 看, 我们发现我们要查询的 \(f\) 总是一段连续的区间, 但是若使用单调队列维护 \(f\) ,\(ap_i\times k\) 又很恶心.
但是我们通过观察, 对于每个 \(ap_i\times k\) 和 \(bp_i \times k\) 所影响到的要查询的 \(f\) , 若 \(j + 1\) , 则它们都会同时加上 \(ap_i\) 或 \(-bp_i\).
比如原先的 \(f(i - w - 1, j) - ap_i,\ f(i - w - 1, j - 1) - 2ap_i,\cdots,f(i - w - 1, j + k) - ap_i\times k\).
当区间右移一次时, 它们的影响变成了:$f(i - w - 1, j - 1) - ap_i,?f(i - w - 1, j - 2) - 2ap_i,\cdots $
所以, 当要查询的区间右移一次时, 它们的相对大小关系是不会改变的, 我们可以对原 \(\rm dp\) 式进行变换来更好的运用这个性质.
以买入为例,\(\max_{k = 1}^{as_i} f(i - w - 1,j - k) - ap_i\times k\), 若令 \(p = j - k\), 则变为:
\[ \begin{array}{} & \max_{p = j - 1}^{j - as_i} f(i - w - 1,p) - ap_i\times (j - p) \\ = & \max_{p = j - 1}^{j - as_i} ( f(i - w - 1,p) + ap_i\times p ) - ap_i\times j \end{array} \]
这么一来,\(f\) 与 \(ap_i\) 就有了一一对应的关系, 可以很容易的利用单调队列优化.
同理, 卖出也可以这样处理, 变式为 \(\max_{p = j + 1}^{j + bs_i} (f(i - w - 1,p) + bp_i\times p) - bp_i\times j\).
接下来就是常规的单调队列优化, 时间复杂度为 \(\Theta (n ^ 2)\).
代码
- #include <bits/stdc++.h>
- using namespace std;
- const int MAXN = 2000 + 5, inf = 0x3f3f3f3f;
- int f[MAXN][MAXN], ap[MAXN], bp[MAXN], as[MAXN], bs[MAXN], w, t, maxp;
- struct monQue {
- pair<int, int> data[MAXN];
- int head, tail;
- void clear() {head = 0, tail = 0;}
- bool empty() {return tail <= head;}
- void push(pair<int, int> x) {
- while(!empty() && data[tail - 1] <= x) tail--;
- data[tail++] = x;
- }
- void pop() {head++;}
- pair<int, int> front() {return data[head];}
- } sell, buy;
- int main() {
- memset(f, ~0x3f, sizeof(f));
- scanf("%d%d%d", &t, &maxp, &w);
- for(int i = 1; i <= t; i++)
- scanf("%d%d%d%d", &ap[i], &bp[i], &as[i], &bs[i]);
- for(int i = 1; i <= t; i++) {
- buy.clear(); sell.clear();
- for(int j = 0; j <= maxp; j++) {
- f[i][j] = max(f[i - 1][j], f[i][j]);
- if(j <= as[i]) f[i][j] = max(-ap[i] * j, f[i][j]);
- if(i - w - 1 <= 0) continue;
- if(j) {
- while(!buy.empty() && buy.front().second <j - as[i]) buy.pop();
- buy.push(make_pair(f[i - w - 1][j - 1] + ap[i] * (j - 1), j - 1));
- f[i][j] = max(buy.front().first - ap[i] * j, f[i][j]);
- }
- }
- if(i - w - 1 <= 0) continue;
- for(int j = maxp - 1; j>= 0; j--) {
- while(!sell.empty() && sell.front().second> j + bs[i]) sell.pop();
- sell.push(make_pair(f[i - w - 1][j + 1] + bp[i] * (j + 1), j + 1));
- f[i][j] = max(sell.front().first - bp[i] * j, f[i][j]);
- }
- }
- int ans = -inf;
- for(int i = 0; i <= maxp; i++) {
- ans = max(f[t][i], ans);
- }
- printf("%d\n", ans);
- return 0;
- }
后记
终于把这个坑填上了..
来源: http://www.bubuko.com/infodetail-2824182.html