题解 P2569 【[SCOI2010]股票交易】

题目链接

Solution [SCOI2010]股票交易

题目大意:给定\(n\)天,每一天有股票买入价格\(AP_i\),买入限制\(AS_i\),卖出价格\(BP_i\),卖出限制\(BS_i\),每次交易后需间隔至少\(w\)天(含\(w\)),手中股票数量不得大于\(MaxP\),求最大收益

动态规划,单调队列


分析:我们很快就可以列出一个\(O(n^3)\)的朴素\(dp\)

\(f[i][j]\)表示到\(i\)天为止,在第\(i\)天时手中有\(j\)张股票的最大收益,然后分类讨论

  • \(1.\)无中生有(),之前啥都不干然后在\(i\)天购买股票:

\(f[i][j] = max(f[i][j],-j \times AP_i) \quad j \in [0,AS_i]\)

  • \(2.\)啥都不干

\(f[i][j] = max(f[i][j],f[i-1][j])\)

  • \(3.\)买股票

\(f[i][j] = max(f[i][j],f[i - w - 1][k]-(j-k) \times AP_i) \quad i > w,k \in [max(0,j - AS_i),j]\)

  • \(4.\)卖股票

\(f[i][j] = max(f[i][j],f[i - w - 1][k]+(k-j) \times BP_i) \quad i > w,k \in[j,min(MaxP,j+BS_i)]\)

我们买卖股票的时候不需要枚举最优最测点的\(i\)(也就是天数),因为它们都被\(i-w-1\)天计算过了

然后状态\(n^2\)多半没法继续优化了,我们考虑从\(O(n)\)转移下手

我们先拆式子,把和决策点相关的部分和跟状态相关的部分分开
\(f[i - w - 1][k]-(j-k) \times AP_i=f[i - w - 1][k] + k \times AP_i-j \times AP_i\)

\(f[i - w - 1][k]+(k-j) \times BP_i=f[i - w - 1][k] + k \times BP_i-j \times BP_i\)

后面和状态有关的常数项暂时忽略,首先,对于一个状态\(f[i][j]\),它的决策点的\(i\)是固定为\(i-w-1\)的,然后我们看\(k\)的取值范围,如果忽略掉上下界的话就是滑动窗口问题,于是我们可以对于每个\(i\)预处理出当前时刻\(i\)所有\(j\)的最优决策

复杂度\(O(n^2)\)

#include 
#include 
#include 
using namespace std;
const int maxn = 2048;
inline int read(){
    int x = 0;char c = getchar();
    while(!isdigit(c))c = getchar();
    while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    return x;
}
int val[2][maxn],q[maxn],ans[2][maxn],f[maxn][maxn],AP[maxn],AS[maxn],BP[maxn],BS[maxn],n,maxp,w,head,tail;
inline void solve(int tim){
    for(int k = 0;k <= maxp;k++)
        val[0][k + 1] = f[tim - w - 1][k] + k * AP[tim],val[1][k + 1] = f[tim - w - 1][k] + k * BP[tim],ans[0][k] = ans[1][k] = -0x7fffffff;
    head = 1,tail = q[0] = 0;
    for(int i = 1;i <= maxp + 1;i++){
        while(head <= tail && val[0][q[tail]] <= val[0][i])tail--;
        q[++tail] = i;
        while(head <= tail && q[head] < i - AS[tim])head++;
        if(head <= tail)ans[0][i - 1] = val[0][q[head]];
    }
    head = 1,tail = q[0] = 0;
    for(int i = maxp + 1;i >= 1;i--){
        while(head <= tail && val[1][q[tail]] <= val[1][i])tail--;
        q[++tail] = i;
        while(head <= tail && q[head] > i + BS[tim])head++;
        if(head <= tail)ans[1][i - 1] = val[1][q[head]];
    }
}
int main(){
    scanf("%d %d %d",&n,&maxp,&w);
    for(int i = 1;i <= n;i++)
        scanf("%d %d %d %d",AP + i,BP + i,AS + i,BS + i);
    for(int i = 1;i <= maxp;i++)f[0][i] = -0x3f3f3f3f;//这里注意,避免不合法状态
    for(int i = 1;i <= n;i++){
        if(i > w)solve(i);
        for(int j = 0;j <= maxp;j++){
            if(j <= AS[i])f[i][j] = -j * AP[i];
            else f[i][j] = -0x3f3f3f3f;
            f[i][j] = max(f[i][j],f[i - 1][j]);
            if(i > w)f[i][j] = max(f[i][j],ans[0][j] - j * AP[i]);
            if(i > w)f[i][j] = max(f[i][j],ans[1][j] - j * BP[i]);
        }
    }
    int ans = 0;
    for(int i = 0;i <= maxp;i++)ans = max(ans,f[n][i]);
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(题解 P2569 【[SCOI2010]股票交易】)