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

链接[SCOI2010] 股票交易

题意

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

通过一段时间的观察,lxhgww预测到了未来 T T 天内某只股票的走势,第 i i 天的股票买入价为每股 A P i AP_i ,第 i i 天的股票卖出价为每股 B P i BP_i (数据保证对于每个 i i ,都有 A P i B P i AP_i \ge BP_i ),但是每天不能无限制地交易,于是股票交易所规定第 i i 天的一次买入至多只能购买 A S i AS_i 股,一次卖出至多只能卖出 B S i BS_i 股。

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

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

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

0 W < T 2000 0\leq W<T\leq 2000
1 MaxP 2000 1\leq\text{MaxP}\leq2000
1 B P i A P i 1000 1\leq BP_i\leq AP_i\leq 1000
1 A S i , B S i MaxP 1\leq AS_i,BS_i\leq\text{MaxP}



分析

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

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


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

  1. i i 天不进行任何交易;
  2. i i 天买入 j k j-k 股,由于交易需间隔 W W 天,故对应 F ( i W 1 , k ) F(i-W-1,k) ,因为 0 < j k A S i 0\lt j-k\le AS_i ,所以 j A S i k < j j-AS_i\le k\lt j
  3. i i 天卖出 k j k-j 股,由于交易需间隔 W W 天,故对应 F ( i W 1 , k ) F(i-W-1,k) ,因为 0 < k j B S i 0\lt k-j\le BS_i ,所以 j < k j + B S i j\lt k\le j+BS_i

状态转移方程:
F ( i , j ) = { F ( i 1 , j ) max j A S i k < j { F ( i W 1 , k ) ( j k ) A P i } max j < k j + B S i { F ( i W 1 , k ) + ( k j ) B P i }                          1 i T , 0 j MaxP F(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 ( 0 , 0 ) = 0 F(0,0)=0 ,其余均为 -\infin
目标 max 0 j MaxP { F ( T , i ) } \max\limits_{0\le j\le \text{MaxP}}\{F(T,i)\}

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

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

以②式为例, F ( i , j ) = max j A S i k < j { F ( i W 1 , k ) ( j k ) A P i } F(i,j)=\max\limits_{j-AS_i\le k\lt j}\{F(i-W-1,k)-(j-k)*AP_i\}

可转化为: F ( i , j ) = max j A S i k < j { G ( i , k ) } j A P i F(i,j)=\max\limits_{j-AS_i\le k\lt j}\{G(i,k)\}-j*AP_i ,其中 G ( i , k ) = F ( i W 1 , k ) + k A P i G(i,k)=F(i-W-1,k)+k*AP_i

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

对③式做相同处理即可,注意处理下标越界情况,最后总的时间复杂度度为 O ( T MaxP ) O(T\cdot\text{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;
}
发布了214 篇原创文章 · 获赞 40 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Ratina/article/details/104101607