HDOJ 3401 Trade(二维DP + 单调队列优化)

题意:

告诉你 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; }
posted @ 2013-02-27 23:06  kedebug  阅读(305)  评论(0编辑  收藏  举报