SCOI2010 股票交易

题目链接

Solution

动态规划题,设 (f_{i,j}) 表示前 (i) 天,手中有 (j) 股的最大钱数。分为 (4) 种情况讨论:

凭空买入

即在当日开始时手中没有股票,当日买入 (j) 股。转移方程:

[f_{i,j}=-j * ap_i ]

不买也不卖

直接转移:

[f_{i,j}=max(f_{i,j},f_{i-1,j}) ]

从前面的某天买入

观察情况 (2),发现 (f_{i,j}≥f_{i-1,j}),所以可以直接用第 (i-w-1) 天更新第 (i) 天,不用考虑前面。转移方程:

[f_{i,j}=max(f_{i,j},f_{i-w-1}{k}-(k-j)*ap_i) (j-as_i≤k<j) ]

从前面的某天卖出

类似于情况 (3),转移方程为:

[f_{i,j}=max(f_{i,j},f_{i-w+1}{k}+(j-k)*bp_i) (j<k≤j+bs_i) ]

优化

这样转移复杂度 (O(n^3)),难以通过 (2000) 的数据,考虑优化:

以情况 (3) 把式子拆开:

[f_{i,j}=max(f_{i,j},f_{i-w-1}{k}+k*ap_i-j*ap_i) ]

(j) 提出来:

[f_{i,j}=max(f_{i,j},f_{i-w-1,k}+k*ap_i)-j*ap_i ]

于是就可以使用单调队列维护。情况 (4) 同理。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
 
const int N = 3333;
int ans = 0, t, maxp, w, head, tail;
int ap[N], bp[N], as[N], bs[N], f[N][N], q[N], p[N];
 
int main()
{
    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]);
    memset(f, -0x3f, sizeof(f));
    for(int i = 1; i <= t; i++)
    {
        for(int j = 0; j <= as[i]; j++) f[i][j] = -j * ap[i];
        for(int j = 0; j <= maxp; j++) f[i][j] = max(f[i][j], f[i - 1][j]);
        if(i - w - 1 <= 0) continue;
        head = 1, tail = 0;
        for(int j = 1; j <= maxp; j++)
        {
            while(head <= tail && p[head] < j - as[i]) head++;
            while(head <= tail && q[tail] <= f[i - w - 1][j - 1] + (j - 1) * ap[i]) tail--;
            q[++tail] = f[i - w - 1][j - 1] + (j - 1) * ap[i], p[tail] = j - 1;
            f[i][j] = max(f[i][j], q[head] - j * ap[i]);
        }
        head = 1, tail = 0;
        for(int j = maxp - 1; j >= 0; j--)
        {
            while(head <= tail && p[head] > j + bs[i]) head++;
            while(head <= tail && q[tail] <= f[i - w - 1][j + 1] + (j + 1) * bp[i]) tail--;
            q[++tail] = f[i - w - 1][j + 1] + (j + 1) * bp[i], p[tail] = j + 1;
            f[i][j] = max(f[i][j], q[head] - j * bp[i]);
        }
    }
    for(int i = 0; i <= maxp; i++) ans = max(ans, f[t][i]);
    printf("%d", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Andy-park/p/12498017.html