题意:
告诉你 T 天里,物品的买卖价格,和最多买卖数量,还有 T 天里能买入最多的物品数量,每一次买卖间隔时间必须大于 w+1
思路:
1. dp[i][j] 表示前 i 天手里有 j 支股票时所获得的最大收益,因为要间隔 w+1 天才能进行一次买卖,所以前 w+1 天只能进行一个操作,那就是买操作。
2. 关于赋初值问题,合法的状态也有可能为负值的,而且结果要取最大值,所以非法状态要赋值为 -INFS.
3. dp[i][j] = max(dp[i-w-1][x] - (j-x)*AP, dp[i-w-1][y] + (y-j)*BP); 另外还有第 i 天什么都不做的情况此时为 dp[i][j] = max(dp[i][j], dp[i-1][j]).
4. 关于转移方程,时间复杂度是很高的,所以要采取单调队列优化,deq[],pos[] 分别放置窗口里面的最大值以及最大值所在的窗口位置。
5. 如何构造单调队列里面的转移,其实很简单例如 dp[3] = max(dp[1] + 2 * v, dp[2] + 1 * v); 两边同时减去 3 * v 就可以变成一个通项公式了,偏移正好。高中数学知识。
// 171ms
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 2010;
const int MAXD = 2010;
const int INFS = 0x3fffffff;
int dp[MAXN][MAXD], deq[MAXN], pos[MAXN];
int main()
{
int cases;
scanf("%d", &cases);
while (cases--)
{
int T, MaxP, W;
scanf("%d %d %d", &T, &MaxP, &W);
int AP, BP, AS, BS;
for (int i = 1; i <= W + 1; ++i)
{
scanf("%d %d %d %d", &AP, &BP, &AS, &BS);
for (int j = 0; j <= MaxP; ++j)
{
if (j <= AS)
dp[i][j] = -j * AP;
else
dp[i][j] = -INFS;
if (i > 1)
dp[i][j] = max(dp[i][j], dp[i-1][j]);
}
}
for (int i = W + 2; i <= T; ++i)
{
scanf("%d %d %d %d", &AP, &BP, &AS, &BS);
int k = i - W - 1;
// buy
int s = 0, e = -1;
for (int j = 0; j <= MaxP; ++j)
{
int val = dp[k][j] + j * AP;
while (s <= e && deq[e] < val)
--e;
deq[++e] = val, pos[e] = j;
while (j - pos[s] > AS)
++s;
dp[i][j] = max(dp[i-1][j], deq[s] - j * AP);
}
// sell
s = 0, e = -1;
for (int j = MaxP; j >= 0; --j)
{
int val = dp[k][j] + j * BP;
while (s <= e && deq[e] < val)
--e;
deq[++e] = val; pos[e] = j;
while (pos[s] - j > BS)
++s;
dp[i][j] = max(dp[i][j], deq[s] - j * BP);
}
}
printf("%d\n", dp[T][0]);
}
return 0;
}