单调队列:是在一个队列中选出的一些元素构成的队列
正因为单调队列能够将一些无用的店删去并且保持了第一个数就是最值
所以我们可以将一些求么某个区间的最值转化成单调队列来做
单调队列优化:
嗯 不知道从哪里盗来的图(很感谢原作者)
这张图能很好的说说明在什么样的情况下去优化
对于图中的dp[i][j]他可以由f[i-1][j-c]到d[i-1][j-1]某一个最值,这里假设最大值;
那么在对于下一个数dp[i][j+1] 他的区间只是往后推了 所以有部分区间是重复的 可以将重复区间进行记录,这个正好是单调队列所拥有的性质
好了回到这倒题本身,这个题是说我们经行股票的买卖,但是我们的每一次的交易与上一次的交易的时间差是不能小于w
求最大获利
嗯 我们可以很快速的想到这道题的dp方程为 dp[i][j]=max{dp[i-1][j](不进行买卖),dp[i-w-1][k]-(j-k)*a[i],dp[i-w-1][k]+(k-j)*b[i]};
很显然这个是n三次转移方程
但是我们对于产生第三个n进行优化,将我们得到的式子进行化简,所以dp[i][j]=dp[i-w-1][k]+k*a[i]-j*a[j],其实我们就是对于每一个i,j求dp[i-w-1][k]+k*a[i]的最大值
所以我们可是令f[i][k]=dp[i-w-1][k]+k*a[i]
对于卖出也是同买入的情况
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct Node{
int x,y;
}a[2001],b[2001];
struct Node1{
int x,t;
}s[2001];
int dp[2001][2001];
int main(){
int T,n,m,w;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&w);
//memset(dp,0,sizeof(dp));
for(int j=1;j<=n;j++){
scanf("%d%d%d%d",&a[j].x,&a[j].y,&b[j].x,&b[j].y);
for(int i=0;i<=m;i++)
dp[j][i]=-1234567890;
}
for(int i=1; i<=w+1; ++i)
for(int j=0; j<=b[i].x; ++j)
dp[i][j]=(-a[i].x*j);
for(int i=2;i<=n;i++){
int l=0,r=-1;
s[0].x=0;
s[0].t=0;
for(int j=0;j<=m;j++)
dp[i][j]=max(dp[i][j],dp[i-1][j]);
if(i-w-1>0){
for(int j=0;j<=m;j++){
int tmp=dp[i-1-w][j]+j*a[i].x;
while(r>=l&&s[r].x<=tmp) r--;
r++;
s[r].x=tmp;
s[r].t=j;
while(s[l].t<j-b[i].x) l++;
dp[i][j]=max(dp[i][j],s[l].x-j*a[i].x);
// cout << l <<"****" << r << endl;
// for(int z=l;z<=r;z++)
// cout << s[z].x << " " << s[z].t << endl;
}
l=0,r=-1;
s[0].x=0;
s[0].t=0;
for(int j=0;j<b[i].y;j++){
int tmp=dp[i-1-w][j]+j*a[i].y;
while(r>=l&&s[r].x<=tmp) r--;
r++;
s[r].x=tmp;
s[r].t=j;
}
for(int j=0;j<=m;j++){
if(j+b[i].y<=m){
int tmp=dp[i-1-w][j+b[i].y]+(j+b[i].y)*a[i].y;
while(r>=l&&s[r].x<=tmp) r--;
r++;
s[r].x=tmp;
s[r].t=j+b[i].y;
}
while(s[l].t<j) l++;
dp[i][j]=max(dp[i][j],s[l].x-j*a[i].y);
}
}
}
int ans=0;
for(int i=0;i<=m;i++)
ans=max(ans,dp[n][i]);
printf("%d\n",ans);
}
}