[SCOI2010] 股票交易

[SCOI2010] 股票交易

P2569 [SCOI2010] 股票交易

单调队列优化 dp 的经典问题。

分析

dp 加单调队列优化。

首先,不难想到是动态规划,看数据范围,可以支持 \(O(n^2)\) 的算法。

第一维肯定是天数,但是显然不够,所以第二维可以是手中的股票数。设 \(f_{i,j}\) 为到第 \(i\) 天为止,手中已有 \(j\) 支股票能赚到的最多的钱。

初始状态,手中并没有任何股票,在某一天 \(i\) 买了 \(j\) 支股票,所以:

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

\[ (0 \le j \le as_i) \]

然后每天只有三种情况,什么都不做,只买,只卖。

  1. 什么都不做,就是和上一天的状态一样,所以:

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

  1. 只买,因为要间隔至少 \(w\) 天,所以可以从 \([0,i-w-1]\) 天转移过来,但是因为上一种情况已经将 \([0,i-w-2]\) 的状态都能转移到第 \(i-w-1\) 天,设第 \(i-w-1\) 天手中有 \(k\) 支股票。因为要买入 \(j-k\) 支股票,再考虑限制,所以 \(0 < j-k \le as_i\),所以:

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

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

\[ (j-as_i \le k \le j-1) \]

  1. 只卖,和上一种情况差不多,要卖出 \(k-j\) 支股票,考虑限制 \(0 < k-j \le bs_i\),所以:

\[ f_{i,j}=\max{(f_{i,j},f_{i-w-1,k}+(k-j) \times bp_i)} \]

\[ f_{i,j}=\max{(f_{i,j},(f_{i-w-1,k}+k \times bp_i)-j \times bp_i)} \]

\[ (j+1 \le k \le j+bs_i) \]

因为 \(k\) 的区间是固定的,可以用单调队列优化掉枚举 \(k\)

考虑一下边界状况:

以情况 \(2\) 为例,考虑 \(j\) 的范围为 \([0,MAXP]\),所以 \(k\) 的范围为 \([j-as_i,j-1]\),全局范围为 \([0,MAXP-1]\),考虑移动 \(k\) 指针,只要 \(k < j-1\) 就能一直移动 \(k\),所以加入队尾,而队头小于 \(j-as_i\) 说明过期要删去,队内单调递减。

在情况 \(3\) 中,\(k\) 的范围为 \([j+1,j+bs_i]\),全局范围为 \([1,MAXP]\),只要 \(k<j+bs_i\)\(k<MAXP\) 时加入队尾,队头小于 \(j+1\) 时说明过期要删去,队内单调递减。

code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>

using namespace std;
typedef long long ll;
const int N=2005;
int n,m,w;
int f[N][N],q[N];
void solve()
{
    cin>>n>>m>>w;
    memset(f,-0x3f,sizeof f);
    for(int i=1;i<=n;i++)
    {
        int ap,bp,as,bs;
        cin>>ap>>bp>>as>>bs;
        for(int j=0;j<=as;j++) f[i][j]=-ap*j;
        for(int j=0;j<=m;j++) f[i][j]=max(f[i][j],f[i-1][j]);
        
        if(i<=w) continue;
        int l=1,r=0,k=-1;
        for(int j=0;j<=m;j++)
        {
            while(k<j-1) 
            {
                k++;
                while(l<=r&&f[i-w-1][q[r]]+q[r]*ap<=f[i-w-1][k]+k*ap) r--;
                q[++r]=k;
            }
            while(l<=r&&q[l]<j-as) l++;
            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,k=0;
        for(int j=0;j<=m;j++)
        {
            while(k<j+bs&&k<m)
            {
                k++;
                while(l<=r&&f[i-w-1][q[r]]+q[r]*bp<=f[i-w-1][k]+k*bp) r--;
                q[++r]=k;
            }
            while(l<=r&&q[l]<j+1) l++;
            if(l<=r) f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*bp-j*bp);
        }
    }
    int ans=0;
    for(int i=0;i<=m;i++) ans=max(ans,f[n][i]);
    cout<<ans<<'\n';
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif 
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}
posted @ 2024-11-19 09:53  zhouruoheng  阅读(7)  评论(0编辑  收藏  举报