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

题目

思路

看到题目和数据范围想到设(f_{i,j})为前(i)天,当前有(j)股股票时的最大收益

容易证明,(j)固定时,(f_{i,j})(i)增加而单调不降(即你可以不买不卖维持现状),那么(f_{i,j})可以从(f_{i-w-1,k})转移而来

(k< j)时为买,(k> j)时为卖,两者受到(as)(bs)的约束,所以(k)的范围为一个区间,用单调队列分别算买和卖两种情况的转移即可

Code

#include<bits/stdc++.h>
#define N 2005
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int n,m,w,ap[N],bp[N],as[N],bs[N];
int f[N][N];
int q[N],l,r;

template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}

int main()
{
	read(n);read(m);read(w);
	memset(f,-50,sizeof(f));
	for(int i=1;i<=n;++i) read(ap[i]),read(bp[i]),read(as[i]),read(bs[i]);
	for(int i = 1 ; i <= n ; i++)
	{
        for(int j=0;j<=as[i];++j) f[i][j]=-1*ap[i]*j;//纯买 
    	for(int j=0;j<=m;++j) f[i][j]=Max(f[i][j],f[i-1][j]);//什么都不做 
        if(i<=w) continue;
        
        l=1;r=0;
        for(int j=0;j<=m;++j)//买 
        {
        	while(l<=r && q[l]+as[i] < j) ++l;
        	while(l<=r && f[i-w-1][q[r]]+q[r]*ap[i] <= f[i-w-1][j]+j*ap[i]) --r;
        	q[++r]=j;
        	if(l<=r) f[i][j]=Max(f[i][j],f[i-w-1][q[l]]+q[l]*ap[i]-j*ap[i]);
		}
        l=1;r=0;
        for(int j=m;j>=0;--j)
        {
        	while(l<=r && q[l]-bs[i] > j) ++l;
        	while(l<=r && f[i-w-1][q[r]]+q[r]*bp[i] <= f[i-w-1][j]+j*bp[i]) --r;
        	q[++r]=j;
        	f[i][j]=Max(f[i][j],f[i-w-1][q[l]]+q[l]*bp[i]-j*bp[i]);
		}
    }
	int maxx=0;
	for(int i=0;i<=m;++i) maxx=Max(maxx,f[n][i]);
	cout<<maxx<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/Chtholly/p/11672943.html