[SCOI2010] 股票交易

[SCOI2010] 股票交易

P2569 [SCOI2010] 股票交易

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

分析

dp 加单调队列优化。

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

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

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

fi,j=apij

(0jasi)

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

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

fi,j=max(fi,j,fi1,j)

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

fi,j=max(fi,j,fiw1,k(jk)×api)

fi,j=max(fi,j,(fiw1,k+k×api)j×api)

(jasikj1)

  1. 只卖,和上一种情况差不多,要卖出 kj 支股票,考虑限制 0<kjbsi,所以:

fi,j=max(fi,j,fiw1,k+(kj)×bpi)

fi,j=max(fi,j,(fiw1,k+k×bpi)j×bpi)

(j+1kj+bsi)

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

考虑一下边界状况:

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

在情况 3 中,k 的范围为 [j+1,j+bsi],全局范围为 [1,MAXP],只要 k<j+bsik<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 @   zhouruoheng  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示