题意:让你炒股票,每天都有买进的额度和价格以及卖出的额度和价格,并规定时间和最多的持有股票是多少,而且买卖操作要隔w+1天求最高的利润
思路:显然分三种情况:不买不卖,买,卖,设dp[i][j]表示第i天持有j股票的最高利润
如果不买不卖的话就是:dp[i][j]=dp[i-1][j]
买: dp[i][j]=max(dp[i][j],dp[pre][k]-(j-k)*ap[i])
卖: dp[i][i]=max(dp[i][j],dp[pre][k]+(k-j)*bp[i])
拿买来说的话:dp[pre][k]-(j-k)*ap[i]=dp[pre][k]-j*ap[i]+k*ap[i],我们的目的是尽量大,所以我们尽量找一个最大的利润,那么维持一个单调队列就是了,重点是队尾的操作,如果后一个比前一个利润高的话,就移动,这样就可以后面的操作数是最优的
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int MAXN = 2010;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
int Time,maxp,w;
int ap[MAXN],bp[MAXN],as[MAXN],bs[MAXN];
int dp[MAXN][MAXN];
pii que[MAXN];
int st,ed;
int main(){
int t;
scanf("%d", &t);
while (t--){
memset(dp[0],-INF,sizeof(dp[0]));
dp[0][0] = 0;
scanf("%d%d%d", &Time, &maxp, &w);
for (int i = 1; i <= Time; i++)
scanf("%d%d%d%d", &ap[i], &bp[i], &as[i], &bs[i]);
int ans = 0;
for (int i = 1; i <= Time; i++){
memcpy(dp[i], dp[i-1], sizeof(dp[i]));
if (i <= w+1){
for (int j = 0; j <= as[i]; j++)
dp[i][j] = max(dp[i-1][j], -ap[i]*j);
continue;
}
st = ed = 0;
int pre = i-w-1;
for (int j = 0; j <= maxp; j++){
pii elem = pii(dp[pre][j]+j*ap[i],j);
que[ed++] = elem;
while (ed-st >= 1 && que[st].second < j-as[i])
st++;
while (ed-st >= 2 && que[ed-1].first >= que[ed-2].first)
que[ed-2] = que[ed-1],ed--;
dp[i][j] = max(dp[i][j], que[st].first-j*ap[i]);
ans = max(ans, dp[i][j]);
}
st = ed = 0;
for (int j = maxp; j >= 0; j--){
pii elem = pii(dp[pre][j]+j*bp[i], j);
que[ed++] = elem;
while (ed-st >= 1 && que[st].second > j+bs[i])
st++;
while (ed-st >= 2 && que[ed-1].first >= que[ed-2].first)
que[ed-2] = que[ed-1],ed--;
dp[i][j] = max(dp[i][j], que[st].first-j*bp[i]);
ans = max(ans, dp[i][j]);
}
}
printf("%d\n", ans);
}
return 0;
}