[SCOI2010]股票交易

简化题意

三个数字 T, maxP, w 分别代表,一共有 T 天,任何时候手中不得超过 maxP 张股票,买卖股票必须在上次买卖之后的 w + 1 天。

然后给你每天的 买入,卖出,最多能买,最多能卖的股票数,分别为 AP, BP,AS,BS。


思路

看看数据范围,二位状态没多大问题,不妨就设 \(f_{i, j}\) 为第 i 天时,手中有 j 张股票的时候最多能赚的钱数。

转移的话,有下边几种情况。

  1. 凭空买。

\(f_{i, j} = -ap_i \times j\big(j \in [0, as_i]\big)\),挺好理解,就是这一天买了 j 张股票,前边几天没买也没卖。

  1. 不买也不卖。

显然就是上前一天的状态, \(f_{i,j} = f_{i-1, j}\)

  1. 在此基础上买股票。

题目中说,两次交易之间至少间隔 w 天,当前是第 i 天,也就是说可以从 i - w - 1 转移过来,设第 i 天的时候一共有 j 个股票, 第 i-w-1 天的时候有 k 张股票,所以转移方程就是:

\[f_{i. j} = \max_{j - as_i \leq k < j} \Big\{ f_{i. j}, f_{i-w-1, k} - (j - k) \times ap_i \Big\} \]

as 为今天最多能买多少股票,应该很好理解。

  1. 在此基础上卖股票。

和上一个差不多,但是卖股票,钱要加上 \(k - j \times bp_i\),所以转移方程就是:

\[f_{i. j} = \max_{j < k \leq j + bs_i} \Big\{ f_{i. j}, f_{i-w-1, k} + (k - j) \times bp_i \Big\} \]

这样你就有 60 分的好成绩了。


考虑如何单调队列优化,我们把上边的式子拆开看看。

\[\begin{aligned} f_{i, j} &= \max_{j - as_i \leq k < j} \Big\{ f_{i. j}, f_{i-w-1, k} - (j - k) \times ap_i \Big\} \\ f_{i, j} &= \max_{j - as_i \leq k < j} \Big\{ f_{i. j}, f_{i-w-1, k} - j \times ap_i + k \times ap_i \Big\} \\ f_{i, j} &= \max_{j - as_i \leq k < j} \Big\{ f_{i. j}, f_{i-w-1, k}+ k \times ap_i \Big\} - j \times ap_i \end{aligned} \]

前边那个 max 可以单调队列优化掉,第四种转移的时候同理。

code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 100010
#define M 2010

using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
int T, maxP, w, l, r;
int ap, bp, as, bs, f[M][M], q[M];

int read() {
  int s = 0, f = 0; char ch = getchar();
  while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
  while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
  return f ? -s : s;
}

int main() {
  T = read(), maxP = read(), w = read();
  memset(f, 128, sizeof f);
  for (int i = 1; i <= T; i++) {
    ap = read(), bp = read(), as = read(), bs = read();
    for (int j = 0 ; j <= as; j++) 
      f[i][j] = -1 * j * ap;
    for (int j = 0 ; j <= maxP; j++) 
      f[i][j] = max(f[i][j], f[i - 1][j]);
    if (i <= w) continue;
    l = 1, r = 0;
    for (int j = 0; j <= maxP; j++) {
      while (l <= r && q[l] < j - as) l++;
      while (l <= r && f[i - w - 1][q[r]] + q[r] * ap <= f[i - w - 1][j] + j * ap) r--;
      q[++r] = j;
      if (l <= r) f[i][j] = max(f[i][j], f[i - w - 1][q[l]] + q[l] * ap - j * ap);
    }
    l = 1, r = 0;
    for (int j = maxP; j >= 0; j--) {
      while (l <= r && q[l] > j + bs) l++;
      while (l <= r && f[i - w - 1][q[r]] + q[r] * bp <= f[i - w - 1][j] + j * bp) r--;
      q[++r] = j;
      if (l <= r) f[i][j] = max(f[i][j], f[i - w - 1][q[l]] + q[l] * bp - j * bp);
    }
  }
  int ans = -inf;
  for (int i = 0; i <= maxP; i++) 
    ans = max(ans, f[T][i]);
  cout << ans;
}
posted @ 2020-11-12 16:16  Kersen  阅读(110)  评论(0)    收藏  举报