题意:链接
方法:单调队列优化DP
解析:噢又是一道情况很多的题,然而三种更新我又落下一种导致样例不过,后来看题解才恍然- -最SB的一种更新居然忘了。
状态好想f[i][j]代表前i天有j双袜子时的最大利润。
三种更新:
第一种:f[i][j]=max(f[i][j],f[i?1][j]);(然而我忘了这一种)
第二种:买入f[i][j]=max(f[i][j],f[i?w?1][k]?(j?k)?a[i].ap)(k>=j?a[i].as);
第三种:卖出f[i][j]=max(f[i][j],f[i?w?1][k]+(k?j)?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);
}
时间: 2024-12-14 09:19:50