P2569 [SCOI2010]股票交易
P2569 [SCOI2010]股票交易
题目链接
数据结构优化DP。
用\(f[i][j]\)表示第\(i\)天手里还有\(j\)张股票的最大收益。
分四种情况转移:
直接买入,不承接之前的:
f[i][j] = -ap * j;
不卖也不买:
f[i][j] = std::max(f[i][j], f[i - 1][j]);
卖出股票:
f[i][j] = std::max(f[i][j], f[i - w - 1][k] + (k - j) * bp);
买入股票,承接之前的:
f[i][j] = std::max(f[i][j], f[i - w - 1][k] - (j - k) * ap);
为啥只能从\(i - w - 1\)转移过来呢?因为\(i - w - 1\)可能是从更前面转移过来的,比如说\(i - w - 2\),这样\(i\)其实也是从\(i - w - 2\)转移过来的。
可以发现,第三种和第四种情况可以把\(j\)提出来:
f[i][j] = max(f[i][j], std::max(f[i - w - 1][k] + k * bp) - j * bp);
f[i][j] = max(f[i][j], std::max(f[i - w - 1][k] + k * ap) - j * ap);
这样我们就可以用单调队列优化了,可以把\(f[i - w - 1][k] + k * bp/ap\)放入单调队列里,每次\(O(1)\)找到最大值,总复杂度\(O(n^2)\)。
#include <bits/stdc++.h>
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 2005;
int T, P, w, l, r, ap, bp, as, bs, ans;
int p[N], f[N][N];
int main() {
T = read(); P = read(); w = read();
memset(f, -0x3f, sizeof(f));
for(int i = 1;i <= T; i++) {
ap = read(), bp = read(), as = read(), bs = read();
for(int j = 0;j <= as; j++) f[i][j] = -ap * j;
for(int j = 0;j <= P; j++) f[i][j] = std::max(f[i][j], f[i - 1][j]);
if(i <= w) continue;
l = 1, r = 0;
for(int j = P;j >= 0; j--) {
while(l <= r && p[l] > j + bs) l++;
// std::cout << l << std::endl;
while(l <= r && f[i - w - 1][p[r]] + p[r] * bp <= f[i - w - 1][j] + j * bp) r--;
p[++r] = j;
if(l <= r) f[i][j] = std::max(f[i][j], f[i - w - 1][p[l]] + p[l] * bp - j * bp);
}
l = 1, r = 0;
for(int j = 0;j <= P; j++) {
while(l <= r && p[l] < j - as) l++;
while(l <= r && f[i - w - 1][p[r]] + p[r] * ap <= f[i - w - 1][j] + j * ap) r--;
p[++r] = j;
if(l <= r) f[i][j] = std::max(f[i][j], f[i - w - 1][p[l]] + p[l] * ap - j * ap);
}
}
for(int i = 0;i <= P; i++) ans = std::max(ans, f[T][i]);
printf("%d", ans);
return 0;
}