[SCOI2010] 股票交易(单调队列优化DP)

链接[SCOI2010] 股票交易

题意

最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。

通过一段时间的观察,lxhgww预测到了未来TTT天内某只股票的走势,第iii天的股票买入价为每股APiAP_iAPi​,第iii天的股票卖出价为每股BPiBP_iBPi​(数据保证对于每个iii,都有APiBPiAP_i \ge BP_iAPi​≥BPi​),但是每天不能无限制地交易,于是股票交易所规定第iii天的一次买入至多只能购买ASiAS_iASi​股,一次卖出至多只能卖出BSiBS_iBSi​股。

另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔WWW天,也就是说如果在第iii天发生了交易,那么从第i+1i+1i+1天到第i+Wi+Wi+W天,均不能发生交易。

同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过MaxP\text{MaxP}MaxP。

在第111天之前,lxhgww手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,TTT天以后,lxhgww想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?

0W<T20000\leq W<T\leq 20000≤W<T≤2000
1MaxP20001\leq\text{MaxP}\leq20001≤MaxP≤2000
1BPiAPi10001\leq BP_i\leq AP_i\leq 10001≤BPi​≤APi​≤1000
1ASi,BSiMaxP1\leq AS_i,BS_i\leq\text{MaxP}1≤ASi​,BSi​≤MaxP



分析

dp状态并不难想,以 iii天 作为 阶段,以 iii天结束时持有jjj股状态,可得:

F(i,j)F(i,j)F(i,j):表示第iii天结束时持有jjj股的最大总收益


共有333种状态转移的情况:

  1. iii天不进行任何交易;
  2. iii天买入jkj-kj−k股,由于交易需间隔WWW天,故对应F(iW1,k)F(i-W-1,k)F(i−W−1,k),因为0<jkASi0\lt j-k\le AS_i0<j−k≤ASi​,所以jASik<jj-AS_i\le k\lt jj−ASi​≤k<j;
  3. iii天卖出kjk-jk−j股,由于交易需间隔WWW天,故对应F(iW1,k)F(i-W-1,k)F(i−W−1,k),因为0<kjBSi0\lt k-j\le BS_i0<k−j≤BSi​,所以j<kj+BSij\lt k\le j+BS_ij<k≤j+BSi​;

状态转移方程:
F(i,j)={F(i1,j)maxjASik<j{F(iW1,k)(jk)APi}maxj<kj+BSi{F(iW1,k)+(kj)BPi}                        1iT,0jMaxPF(i,j)=\begin{cases} F(i-1,j)\\ \max\limits_{j-AS_i\le k\lt j}\{F(i-W-1,k)-(j-k)*AP_i\}\\ \max\limits_{j\lt k\le j+BS_i}\{F(i-W-1,k)+(k-j)*BP_i\} \\\end{cases}\;\;\;\;\;\;\;\;\;\;\;\;1\le i\le T,0\le j\le \text{MaxP}F(i,j)=⎩⎪⎪⎨⎪⎪⎧​F(i−1,j)j−ASi​≤k<jmax​{F(i−W−1,k)−(j−k)∗APi​}j<k≤j+BSi​max​{F(i−W−1,k)+(k−j)∗BPi​}​1≤i≤T,0≤j≤MaxP
初值F(0,0)=0F(0,0)=0F(0,0)=0,其余均为-\infin−∞;
目标max0jMaxP{F(T,i)}\max\limits_{0\le j\le \text{MaxP}}\{F(T,i)\}0≤j≤MaxPmax​{F(T,i)};

直接采用三重循环的时间复杂度会达到O(TMaxP2)O(T\cdot\text{MaxP}^2)O(T⋅MaxP2),故需要对其进行优化,显然状态转移方程的②式和③式满足单调队列优化的条件 (1D/1D动态规划优化),当固定阶段iii时,有:

  1. 决策kkk的上下边界是关于状态jjj的一次函数
  2. F(iW1,k)F(i-W-1,k)F(i−W−1,k)外的冗余部分是关于jjj和kkk的一次函数

以②式为例,F(i,j)=maxjASik<j{F(iW1,k)(jk)APi}F(i,j)=\max\limits_{j-AS_i\le k\lt j}\{F(i-W-1,k)-(j-k)*AP_i\}F(i,j)=j−ASi​≤k<jmax​{F(i−W−1,k)−(j−k)∗APi​}

可转化为:F(i,j)=maxjASik<j{G(i,k)}jAPiF(i,j)=\max\limits_{j-AS_i\le k\lt j}\{G(i,k)\}-j*AP_iF(i,j)=j−ASi​≤k<jmax​{G(i,k)}−j∗APi​,其中G(i,k)=F(iW1,k)+kAPiG(i,k)=F(i-W-1,k)+k*AP_iG(i,k)=F(i−W−1,k)+k∗APi​;

对于 任意jjj,第一部分的值是相等的,最优决策kkk只需要满足在范围内即可,故只需要维护 一个G(i,k)G(i,k)G(i,k)递减,k[jASi,j)k\in[j-AS_i,j)k∈[j−ASi​,j)的单调队列 即可。

对③式做相同处理即可,注意处理下标越界情况,最后总的时间复杂度度为O(TMaxP)O(T\cdot\text{MaxP})O(T⋅MaxP)



代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
const int maxn=2e3+10;
int T,MaxP,W;
int AP[maxn],BP[maxn],AS[maxn],BS[maxn];
int F[maxn][maxn];
int GA(int i,int k)
{
    return F[max(0,i-W-1)][k]+k*AP[i];
}
int GB(int i,int k)
{
    return F[max(0,i-W-1)][k]+k*BP[i];
}
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,0xcf,sizeof(F));
    F[0][0]=0;
    for(int i=1;i<=T;i++)
    {
        //第i天不交易
        for(int j=0;j<=MaxP;j++)
            F[i][j]=F[i-1][j];
 
 
        //第i天买入j-k股
        deque<int> q;
        for(int j=0;j<=MaxP;j++)
        {
            while(!q.empty()&&q.front()<j-AS[i])     //保证下界j-AS[i]
                q.pop_front();
            if(!q.empty())
                F[i][j]=max(F[i][j],GA(i,q.front())-j*AP[i]);
            while(!q.empty()&&GA(i,q.back())<=GA(i,j))
                q.pop_back();
            q.push_back(j);      //新加入的k一定小于j,保证了上界j
        }
 
        //第i天卖出k-j股
        q.clear();
        for(int k=1;k<=min(MaxP,BS[i]);k++)
        {
            while(!q.empty()&&GB(i,q.back())<=GB(i,k))
                q.pop_back();
            q.push_back(k);
        }
        for(int j=0;j<=MaxP;j++)
        {
            while(!q.empty()&&q.front()<=j)      //保证下界j
                q.pop_front();
            if(!q.empty())
                F[i][j]=max(F[i][j],GB(i,q.front())-j*BP[i]);
            if(j+BS[i]+1<=MaxP)      
            {
                while(!q.empty()&&GB(i,q.back())<=GB(i,j+BS[i]+1))
                    q.pop_back();
                q.push_back(j+BS[i]+1);    //新加入的k一定小于等于j+BS[i],保证了上界j+BS[i]
            }
        }
    }
    int ans=0;
    for(int j=0;j<=MaxP;j++)
        ans=max(ans,F[T][j]);
    printf("%d\n",ans);
    return 0;
}
[SCOI2010] 股票交易(单调队列优化DP)[SCOI2010] 股票交易(单调队列优化DP) 墓华 发布了214 篇原创文章 · 获赞 40 · 访问量 2万+ 私信 关注
上一篇:luogu 4886


下一篇:动态规划