BZOJ 1855 [Scoi2010]股票交易 单调队列优化DP

题意:链接

方法:单调队列优化DP

解析:噢又是一道情况很多的题,然而三种更新我又落下一种导致样例不过,后来看题解才恍然- -最SB的一种更新居然忘了。

状态好想f[i][j]代表前i天有j双袜子时的最大利润。

三种更新:

第一种: f[i][j]=max(f[i][j],f[i1][j]) ;(然而我忘了这一种)

第二种:买入 f[i][j]=max(f[i][j],f[iw1][k](jk)a[i].ap)(k>=ja[i].as) ;

第三种:卖出 f[i][j]=max(f[i][j],f[iw1][k]+(kj)a[i].bp)(k<=max(maxp,j+a[i].bs))

然后我们观察式子,第一种更新O(1)完成,第二和第三的时候如果枚举k的话复杂度承受不了,所以考虑怎么优化,显而易见第二三种是线性的,所以考虑到队列可不可行?于是整理表达式,发现可行,则按照f[i-w-1][k]+k*a[i].ap以及f[i-w-1][k]+k*a[i].bp维护递减就可以。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2010
using namespace std;
int t,maxp,w;
int f[N][N];
int q[N];
struct node
{
    int ap,bp,as,bs;
}a[N];
int main()
{
    scanf("%d%d%d",&t,&maxp,&w);
    for(int i=1;i<=t;i++)
    {
        scanf("%d%d%d%d",&a[i].ap,&a[i].bp,&a[i].as,&a[i].bs);
    }
    memset(f,-0x3f,sizeof(f));
    for(int i=1;i<=t;i++)
    {
        for(int j=0;j<=a[i].as;j++)f[i][j]=-a[i].ap*j;
        for(int j=0;j<=maxp;j++)f[i][j]=max(f[i][j],f[i-1][j]);
        if(i-w-1>=0)
        {
            int head=0,tail=0;
            for(int j=0;j<=maxp;j++)
            {
                while(head<tail&&q[head]<j-a[i].as)head++;
                while(head<tail&&f[i-w-1][j]+j*a[i].ap>=f[i-w-1][q[tail-1]]+q[tail-1]*a[i].ap)tail--;
                q[tail++]=j;
                if(head<tail)f[i][j]=max(f[i][j],f[i-w-1][q[head]]-(j-q[head])*a[i].ap);
            }
            head=0,tail=0;
            for(int j=maxp;j>=0;j--)
            {
                while(head<tail&&q[head]>j+a[i].bs)head++;
                while(head<tail&&f[i-w-1][j]+j*a[i].bp>=f[i-w-1][q[tail-1]]+q[tail-1]*a[i].bp)tail--;
                q[tail++]=j;
                if(head<tail)f[i][j]=max(f[i][j],f[i-w-1][q[head]]+(q[head]-j)*a[i].bp);
            }
        }
    }
    int ans=0;
    for(int i=0;i<=maxp;i++)
    {
        ans=max(ans,f[t][i]);
    }
    printf("%d\n",ans);
}

你可能感兴趣的:(更新,方法,解析,max,队列)