传送门:点击打开链接
题意:有种股票,现在知道n天,拥有的股票股数必须<=V,每两次操作时间之差必须大于W。接下来n天,分别是当天股票买和卖的价格,以及买和卖的最大上限。刚开始认为钱是INF的,问最后能赚多少钱。
思路:一道非常好的单调队列优化dp的好题!
首先,dp无非就两种设法,
1.dp[i][j]表示最后一次操作为第i天,已经拥有了j只股票的赚的最大钱数.
2.dp[i][j]前i天里,已经拥有了j只股票的赚的最大钱数.
#include<map> #include<set> #include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int>PII; const int MX = 2e3 + 5; const int INF = 0x3f3f3f3f; int n, V, W; int dp[MX][MX]; int c, r; int AP[MX], BP[MX], AS[MX], BS[MX]; void umax(int &a, int b) { a = a > b ? a : b; } struct Data { int id, x; Data() {} Data(int _id, int _x) { id = _id; x = _x; } } Q[MX]; int solve() { for(int i = 1; i <= W + 1; i++) { for(int j = 0; j <= V; j++) { dp[i][j] = j <= AS[i] ? -j * AP[i] : -INF; if(i > 1) umax(dp[i][j], dp[i - 1][j]); } } for(int i = W + 2; i <= n; i++) { for(int j = 0; j <= V; j++) { dp[i][j] = dp[i - 1][j]; } c = r = 0; Q[r++] = Data(0, dp[i - W - 1][0]); for(int j = 1; j <= V; j++) { while(c < r && j - Q[c].id > AS[i]) c++; int Max = Q[c].x; umax(dp[i][j], Max - j * AP[i]); Data temp(j, dp[i - W - 1][j] + j * AP[i]); while(c < r && Q[r - 1].x < temp.x) r--; Q[r++] = temp; } c = r = 0; Q[r++] = Data(V, dp[i - W - 1][V] + V * BP[i]); for(int j = V - 1; j >= 0; j--) { while(c < r && Q[c].id - j > BS[i]) c++; int Max = Q[c].x; umax(dp[i][j], Max - j * BP[i]); Data temp(j, dp[i - W - 1][j] + j * BP[i]); while(c < r && Q[r - 1].x < temp.x) r--; Q[r++] = temp; } } return dp[n][0]; } int main() { int T; //FIN; scanf("%d", &T); while(T--) { scanf("%d%d%d", &n, &V, &W); for(int i = 1; i <= n; i++) { scanf("%d%d%d%d", &AP[i], &BP[i], &AS[i], &BS[i]); } printf("%d\n", solve()); } return 0; }