Solution
动态规划题,设 (f_{i,j}) 表示前 (i) 天,手中有 (j) 股的最大钱数。分为 (4) 种情况讨论:
凭空买入
即在当日开始时手中没有股票,当日买入 (j) 股。转移方程:
[f_{i,j}=-j * ap_i
]
不买也不卖
直接转移:
[f_{i,j}=max(f_{i,j},f_{i-1,j})
]
从前面的某天买入
观察情况 (2),发现 (f_{i,j}≥f_{i-1,j}),所以可以直接用第 (i-w-1) 天更新第 (i) 天,不用考虑前面。转移方程:
[f_{i,j}=max(f_{i,j},f_{i-w-1}{k}-(k-j)*ap_i) (j-as_i≤k<j)
]
从前面的某天卖出
类似于情况 (3),转移方程为:
[f_{i,j}=max(f_{i,j},f_{i-w+1}{k}+(j-k)*bp_i) (j<k≤j+bs_i)
]
优化
这样转移复杂度 (O(n^3)),难以通过 (2000) 的数据,考虑优化:
以情况 (3) 把式子拆开:
[f_{i,j}=max(f_{i,j},f_{i-w-1}{k}+k*ap_i-j*ap_i)
]
把 (j) 提出来:
[f_{i,j}=max(f_{i,j},f_{i-w-1,k}+k*ap_i)-j*ap_i
]
于是就可以使用单调队列维护。情况 (4) 同理。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 3333;
int ans = 0, t, maxp, w, head, tail;
int ap[N], bp[N], as[N], bs[N], f[N][N], q[N], p[N];
int main()
{
scanf("%d%d%d", &t, &maxp, &w);
for(int i = 1; i <= t; i++)
scanf("%d%d%d%d", &ap[i], &bp[i], &as[i], &bs[i]);
memset(f, -0x3f, sizeof(f));
for(int i = 1; i <= t; i++)
{
for(int j = 0; j <= as[i]; j++) f[i][j] = -j * ap[i];
for(int j = 0; j <= maxp; j++) f[i][j] = max(f[i][j], f[i - 1][j]);
if(i - w - 1 <= 0) continue;
head = 1, tail = 0;
for(int j = 1; j <= maxp; j++)
{
while(head <= tail && p[head] < j - as[i]) head++;
while(head <= tail && q[tail] <= f[i - w - 1][j - 1] + (j - 1) * ap[i]) tail--;
q[++tail] = f[i - w - 1][j - 1] + (j - 1) * ap[i], p[tail] = j - 1;
f[i][j] = max(f[i][j], q[head] - j * ap[i]);
}
head = 1, tail = 0;
for(int j = maxp - 1; j >= 0; j--)
{
while(head <= tail && p[head] > j + bs[i]) head++;
while(head <= tail && q[tail] <= f[i - w - 1][j + 1] + (j + 1) * bp[i]) tail--;
q[++tail] = f[i - w - 1][j + 1] + (j + 1) * bp[i], p[tail] = j + 1;
f[i][j] = max(f[i][j], q[head] - j * bp[i]);
}
}
for(int i = 0; i <= maxp; i++) ans = max(ans, f[t][i]);
printf("%d", ans);
return 0;
}