[BZOJ 1855][SCOI 2010]股票交易(单调队列优化DP)

来源:互联网 发布:电脑淘宝闺蜜投诉入口 编辑:程序博客网 时间:2024/06/05 15:40

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1855

思路

很显然是个DP题,比较容易想到下面的DP做法:
f[i][j]表示第i天,手上有j个股票的最大获利。显然最终的答案为max{f[i][0]}(显然以某天交易结束后收手不干,肯定是手上没有股票是最优的),DP初始化如下:

f(x)={f[i][j]=max{f[i1][j],apj},j<=as(i)f[i][j]=f[i1][j],j>as(i)

而DP方程则为(以转移情况为购入股票举例)

f[i][j]=max{f[iw1][k]+ap(jk)}

这样做的正确性是显然的,f[i][j]一定是前i天里手持j股的最优解,这样一来我们就节省了一维时间复杂度,但是还是会TLE。

变化一下这个DP方程:

f[i][j]=max{f[iw1][k]apk+apj}

一般可以用单调队列优化的DP都能满足形如f[x]=max(min){f[k]}+g[x]的形式,那么我们可以把这里的f[iw1][k]看作是上面的f[k]apj看作是上面的g[x],于是就可以从小到大枚举j,并用单调队列维护二元组(k,f[iw1][k]),并保证队首的f[iw1][k]是最大的,DP到某个j时,先把队首所有不合法的f[iw1][k]全部弹出,然后用第一个合法的队首去更新f[i][j],然后将这时的f[iw1][j]放入队尾,并同时维护队列单调性。

而卖出的做法也非常相似,在这里就不再赘述了

代码

刚开始数组开大了,直接卡成TLE。。。郁闷。。。

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXN 2100#define INF 0x3f3f3f3fusing namespace std;pair<int,int>q[MAXN];int f[MAXN][MAXN];int T,maxP,w;int main(){    int ans=-INF;    memset(f,-INF,sizeof(f));    scanf("%d%d%d",&T,&maxP,&w);    for(int i=1;i<=T;i++)    {        int ap,bp,as,bs;        scanf("%d%d%d%d",&ap,&bp,&as,&bs);        for(int j=0;j<=as;j++) f[i][j]=-ap*j; //初始化f[i][j]=-ap*j(光买了股票)        for(int j=0;j<=maxP;j++) f[i][j]=max(f[i][j],f[i-1][j]); //初始化什么都没买的情况更新f数组        int k=i-w-1; //单调队列里保存的就是(j,f[w][j]+ap[i]*j(-bp[i]*j))这个二元组        if(k>=0)        {            int h=0,t=0;            for(int j=0;j<=maxP;j++)            {                //买入股票                while(h<t&&q[h].first<j-as) h++;                while(h<t&&q[t-1].second<=f[k][j]+ap*j) t--;                q[t++]=make_pair(j,f[k][j]+ap*j);                if(h<t) f[i][j]=max(f[i][j],q[h].second-ap*j);            }            h=0,t=0;            for(int j=maxP;j>=0;j--)            {                //卖出股票                while(h<t&&q[h].first>j+bs) h++; //!!!!!                while(h<t&&q[t-1].second<=f[k][j]+bp*j) t--;                q[t++]=make_pair(j,f[k][j]+bp*j);                if(h<t) f[i][j]=max(f[i][j],q[h].second-bp*j);            }        }        ans=max(ans,f[i][0]); //显然以某天交易结束后收手不干,肯定是手上没有股票是最优的    }    printf("%d\n",ans);    return 0;}
0 0
原创粉丝点击