[SCOI2010] 股票交易

题目类型:DP+单调队列优化

传送门:>Here<

题意:共有\(T\)天,每天可以选择买股票或卖股票。如果有一天买了或卖了则称这一天是进行交易的。任何进行交易的两天之间必须间隔\(W\)天。第\(i\)天最多买进\(as\)股,卖出\(bs[i]\)股。第\(i\)天的进价为\(ap[i]\),卖出价是\(bp[i]\)。且规定任何一天都不可以拥有超过\(maxP\)股。在如上的条件限制下,要求\(T\)天后最多赚多少

解题思路

题目就烦的要命

读了几遍后,自然而然想到\(dp[i][j]\)表示前\(i\)天的最大收益,且第\(i\)天拥有\(j\)股。这样转移就非常方便了,无非分为那么几种情况

  • 不买不卖。选择继承,则有\[dp[i][j]=dp[i-1][j]\]

  • 买进。这里的买进要分两种情况来讨论,在前\(W+1\)天里,只能买进而不能卖出,并且只有一天能够交易。因此买进全都是亏钱的。这个部分可以作为初始化:\[dp[i][j]=-j*AP[i]\]要么就是在后来,可以在已有的基础上选择买进。假设是在拥有\(p\)股的基础上的,则\[dp[i][k]=Max\{dp[i-W-1][p]-(k-p)*AP[i]\}\]注意我们并没有枚举从哪一天转移而来。为什么?因为对于所有\(i<j\),一定满足\(dp[i][x] \leq dp[j][x]\)。因为这之间我们可以选择不买,也就是继承。只要是继承就不可能变小,因此没有必要枚举从哪一天转移来了

  • 卖出。卖出就不可能在前\(W+1\)天了,因此只需要考虑有基础的情况。设原有\(q\)股,则\[dp[i][k]=Max\{dp[i-W-1][q]+(q-k)*BP[i]\}\]

确定了基本方程以后,我们还需要知道各个循环变量的范围。\(i,k\)的范围都显然,关键是\(p,q\)

先来考虑\(p\)。既然是选择买进,所以一定\(p<k\)。并且那一天最多买进\(AS[i]\),所以有不等式\(k-p \leq AS[i]\)成立,也就是\(p \geq k-AS[i]\)因此\[k-AS[i] \leq p < k\]

那么\(q\)也一样。易得\(q>k\),并且\(q-k \leq BS[i]\)。因此\[k<q \leq BS[i]+k\]

至此,复杂度\(O(n^3)\),能拿到\(70\)

考虑优化。我们就买进的方程进行讨论,观察方程我们发现,这个方程的实质就是\(p\)在范围\([k-AS[i],k)\)中取\(dp[i-W-1][p]-(k-p)*AP[i]\)的最大值。很容易让我们想到滑动窗口,对吗?因此我们提取出只与\(p\)有关的项,将方程化为\(dp[i-W-1][p]+p*AP[i] - k*AP[i]\)。并且我们发现滑动的正好是扫描\(k\)的那一层,也就是\(i\)是确定的。换句话说\(p\)\(k\)的过去。因此维护滚动最大值即可!

对于\(q\)也一样,但是\(q\)是需要由大的往小的转移,因此应当倒着扫。

Code

边界条件还是非常难的,还要多加谨慎

/*By DennyQi 2018.8.21*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 2010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x<<3) + (x<<1) + c - '0', c = getchar(); return x * w;
}
int T,maxP,W,P,Q;
int AP[MAXN],BP[MAXN],AS[MAXN],BS[MAXN];
int dp[MAXN][MAXN],q[2][MAXN],h[2],t[2];
int main(){
    T = r, maxP = r, W = r;
    memset(dp, -0x3f, sizeof dp);
    for(int i = 1; i <= T; ++i){
        AP[i] = r, BP[i] = r;
        AS[i] = r, BS[i] = r;
    }
    dp[0][0] = 0;
    for(int i = 1; i <= T; ++i){
        for(int j = 0; j <= AS[i]; ++j) dp[i][j] = -j * (AP[i]);
        for(int k = 0; k <= maxP; ++k) dp[i][k] = Max(dp[i][k], dp[i-1][k]);
        h[0] = h[1] =  1, t[0] = t[1] = 0;
        if(i-W-1 < 0) continue;
        for(int k = 0; k <= maxP; ++k){
            while(h[0] <= t[0] && q[0][h[0]] < k - AS[i]) ++h[0];
            while(h[0] <= t[0] && dp[i-W-1][q[0][t[0]]] + q[0][t[0]]*AP[i] <= dp[i-W-1][k]+k*AP[i]) --t[0];
            q[0][++t[0]] = k;
            if(h[0] <= t[0]) dp[i][k] = Max(dp[i][k], dp[i-W-1][q[0][h[0]]] - (k-q[0][h[0]]) * AP[i]);
        }
        for(int k = maxP; k >= 0; --k){
            while(h[1] <= t[1] && q[1][h[1]] > BS[i]+k) ++h[1];
            while(h[1] <= t[1] && dp[i-W-1][q[1][t[1]]] + q[1][t[1]]*BP[i] <= dp[i-W-1][k]+k*BP[i]) --t[1];
            q[1][++t[1]] = k;
            if(h[1] <= t[1]) dp[i][k] = Max(dp[i][k], dp[i-W-1][q[1][h[1]]] + (q[1][h[1]]-k) * BP[i]);
        }
    }
    int ans = 0;
    for(int i = 0; i <= maxP; ++i){
        ans = Max(ans, dp[T][i]);
    }
    printf("%d", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/9509526.html