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股票交易的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P2569 [SCOI2010]股票交易

Luogu 2569 SCOI2010 股票交易

[SCOI2010]股票交易

BZOJ1855[Scoi2010]股票交易 DP+单调队列

1855: [Scoi2010]股票交易[单调队列优化DP]

[SCOI2010]股票交易