Luogu 2569 SCOI2010 股票交易

题面

这个题状态设计和状态转移方程都非常好设计好推,重点思考一下单调队列优化以及代码DP中一些初始化,转移要点的一些方法,尽量做到以后不再犯类似的错误。

F[i][j] 即第i天,当前手中有j的股票所能带来的最大价值。

分类讨论一下,今天可以买,可以不买,还可以卖。

买的话可以分为我今天第一次开始买(是不是发现它其实就是初始化),和以前买过了,这就需要从先前的转移过来了。

不买就直接取Max

卖掉的话就枚举一下卖了多少,然后看一下可以收益的最大价值。

这里就可以体现一个很有用的思想,既然总是初始化有问题,就可以把初始化部分也归为一个转移来讨论,就像本题中这样,这样我们讨论当前天买股票时以前没买过,自然也就是初始化了。

在转移买股票的时候要注意一下股票持有数量的范围的枚举,既然当前天是以前卖过的,我们的K值是在这个J值上单调向右滑的,以前的K值较大,所以我们肯定要让左边的小一些。

然后单调队列里面维护的就是这个K值,注意在单调队列维护完之后转移方程写原方程中,把原来的K变成对头就行了。

代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 2020;
int T, MaxP, W;
 
int ap[N], bp[N], as[N], bs[N], ans;
//在第i天的时候,手上持有j股票,
int f[N][N];

//最近DP写的总是推的出式子,其他各种各样出问题

int q[N];//单调队列优化
int h, t;
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, 128, sizeof f);//求Max,而且初始买票需要花钱,所以赋值成负无穷
    for(int i = 1;i <= T;++ i){
        for(int j = 0;j <= as[i];++ j){//buy  today
            f[i][j] = -1 * j * ap[i];
        }
        for(int j = MaxP;j >= 0;-- j)
        f[i][j] = max(f[i][j], f[i-1][j]);//正序倒序无所谓
        if(i - W - 1 <= 0) continue;
        //单调队列的话我们可以这么考虑,我们优化完毕之后
        //发现k满足单调性以及维护的一坨是和k相关的,所以我们可以在
        //队列里面维护的东西就是当前今天的这个k值,更新一下
        h = 1,t = 0;
        for(int j = 0;j <= MaxP;++ j){  
            while(h <= t && f[i-W-1][q[t]] + ap[i] * q[t] <= f[i-W-1][j] + ap[i] * j)
            t --;
            q[++t] = j;
            while(h <= t && q[h] < j - as[i])//对头过时了,买这么多也买不着
            h ++;
            if(h <= t) f[i][j] = max(f[i][j], f[i-W-1][q[h]] - ap[i] * (j - q[h]));
        }
        h = 1,t = 0;
        for(int j = MaxP;j >= 0;-- j){
            while(h <= t && f[i-W-1][q[t]] + bp[i] * q[t] <= f[i-W-1][j] + bp[i] * j)
            t --;
            q[++ t] = j;
            while(h <= t && q[h] > j + bs[i]) 
            h ++;
            if(h <= t) f[i][j] = max(f[i][j], f[i-W-1][q[h]] + bp[i] * (q[h] - j));
        }
        /*for(int j = 0;j <= MaxP;++ j){
            f[i][j] = max(f[i-1][j], f[i][j]);//didn' buy anything
            for(int k = 0;k <= MaxP;++ k){//k was last money, j is now
                if(i <= W) continue;
                if(j - k <= as[i])
                f[i][j] = max(f[i][j],f[i-W-1][k] - ap[i] * (j - k));
                //F[i][j] = max(f[i-W-1][k] - ap[i]*j + ap[i][k]);
                //F[i][j] = max(f[i-W-1][k] + ap[i]*k) - ap[i]*j;
                //j - as[i] <= k < j 单调性来了
                if(k - j <= bs[i])
                f[i][j] = max(f[i][j],f[i-W-1][k] + bp[i] * (k - j));
                //F[i][j] = max(f[i-W-1][k] + bp[i]k - bp[i]j);
                //F[i][j] = max(f[i-W-1][k] + bp[i]*k) - bp[i]*j;
                //j < k <= j + bs[i] 单调性 
            }
        */
        }
    ans = 0;
    for(int i = 0;i <= MaxP;++ i)
    ans = max(ans, f[T][i]);
    printf("%d", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Shu-Kuang/p/12828933.html