SCOI 2010股票交易
Posted zhylj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SCOI 2010股票交易相关的知识,希望对你有一定的参考价值。
题目
最近 ( ext{lxhgww}) 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。
通过一段时间的观察,( ext{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) 天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过 ( ext{MaxP})。
在第 (1) 天之前,( ext{lxhgww}) 手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,(T) 天以后,( ext{lxhgww}) 想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?
分析
设 (f(i,j)) 代表第 (i) 持有 (j) 股的最大收益。
[
f(i,j) = maxleft{ egin{array}{}
max_{k = 1}^{as_i} f(i - w - 1,j - k) - ap_i imes k \max_{k = 1}^{bs_i} f(i - w - 1,j + k) + bp_i imes k f(i - 1,j)
end{array}
ight.
]
很容易得出,该方程的时间复杂度为 (Theta (n^3))。显然会(
m T)。
以只买入的 (max_{k = 1}^{as_i} f(i - w - 1,j - k) - ap_i imes k) 看,我们发现我们要查询的 (f) 总是一段连续的区间,但是若使用单调队列维护 (f) ,(ap_i imes k) 又很恶心。
但是我们通过观察,对于每个 (ap_i imes k) 和 (bp_i imes 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 imes k)。
当区间右移一次时,它们的影响变成了:$f(i - w - 1, j - 1) - ap_i,?f(i - w - 1, j - 2) - 2ap_i,cdots $
所以,当要查询的区间右移一次时,它们的相对大小关系是不会改变的,我们可以对原 ( m dp) 式进行变换来更好的运用这个性质。
以买入为例,(max_{k = 1}^{as_i} f(i - w - 1,j - k) - ap_i imes k),若令 (p = j - k),则变为:
[
egin{array}{}
& max_{p = j - 1}^{j - as_i} f(i - w - 1,p) - ap_i imes (j - p) \
= & max_{p = j - 1}^{j - as_i} ( f(i - w - 1,p) + ap_i imes p ) - ap_i imes j
end{array}
]
这么一来,(f) 与 (ap_i) 就有了一一对应的关系,可以很容易的利用单调队列优化。
同理,卖出也可以这样处理,变式为 (max_{p = j + 1}^{j + bs_i} (f(i - w - 1,p) + bp_i imes p) - bp_i imes 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
", ans);
return 0;
}
后记
终于把这个坑填上了。。
以上是关于SCOI 2010股票交易的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ1855[Scoi2010]股票交易 DP+单调队列