【题目链接】 点击打开链接
【题意】 大概就是说一个人现在炒股,他有两种操作,一种是买股票(这个当然是花钱), 一个是卖股票(这里钦定为挣钱)。然后每个人在每天买和卖股票的数量是有限制的,并且还有一个限制是一个人拥有的股票数不能超过MaxP。还有一个人执行操作的间隔必须大于W。问这个人最后最多挣多少钱?
【解题方法】
我们可以大胆的写出一个朴素的DP方程:
dp[i][j] 表示第 i 天,拥有股票数目为 j 的时候赚了多少钱?
那么DP分为3种情况:
(1) 前一天不买不卖,从前一天转移过来
dp[i][j] = max(dp[i][j], dp[i-1][j])
(2) 前i - w - 1天买进一些股。
dp[i][j] = max(dp[i][j], dp[i - w - 1][k] + (j - k) * AP[i]);
(3) 前i - w - 1天卖掉一些股。
dp[i][j] = max(dp[i][j], dp[i - w - 1][k] + (k - j) * BP[i]);
好的,到现在为止,我们的朴素DP方程就推完了,或许这里有个疑惑?为什么是从i - w - 1转移就行了呢? 这里考虑下 i - w - 1的状态 可以由 i - w - 2的状态不买不卖,
转移过来, 那么我们就可以放心的使用这个DP了。
我们来想一下这个DP的复杂度?
枚举i, j, k 显然是 O(Maxp * n * n), 这显然炸掉的复杂度, 题目只有1000ms。那么我们怎么优化呢? 我们把DP来变一下形状,发现第2个dp可以写成 :
dp[i][j] = max(dp[i - w - 1][k] - (j * AP[i] - k * AP[i])
= dp[i - w - 1][k] + k * AP[i] - j * AP[i];
让f[i - 1][j] = dp[i - w - 1][k] + k * AP[i];
g[i][j] = - j * AP[i];
这个式子又转化成了标准的单调队列优化,我们的复杂度可以顺利降为 O(MaxP * n), 就可以通过这个题了。
【AC代码】
//
//Created by BLUEBUFF 2016/1/8
//Copyright (c) 2016 BLUEBUFF.All Rights Reserved
//
#pragma comment(linker,"/STACK:102400000,102400000")
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <time.h>
#include <cstdlib>
#include <cstring>
#include <sstream> //isstringstream
#include <iostream>
#include <algorithm>
using namespace std;
//using namespace __gnu_pbds;
typedef long long LL;
typedef pair<int, LL> pp;
#define REP1(i, a, b) for(int i = a; i < b; i++)
#define REP2(i, a, b) for(int i = a; i <= b; i++)
#define REP3(i, a, b) for(int i = a; i >= b; i--)
#define CLR(a, b) memset(a, b, sizeof(a))
#define MP(x, y) make_pair(x,y)
const int maxn = 220;
const int maxm = 2e5;
const int maxs = 10;
const int maxp = 1e3 + 10;
const int INF = 1e9;
const int UNF = -1e9;
const int mod = 1e9 + 7;
int gcd(int x, int y) {return y == 0 ? x : gcd(y, x % y);}
//typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>order_set;
//head
struct node{
int pos, f;
}que1[2010];//维护单调递减栈
int AP[2010], BP[2010], AS[2010], BS[2010];
int dp[2010][2010];
int n, MaxP, w, head1, tail1;
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d", &n, &MaxP, &w);
for(int i = 1; i <= n; i++) scanf("%d%d%d%d", &AP[i], &BP[i], &AS[i], &BS[i]);
//init
head1 = tail1 = 0;
CLR(que1, 0);
for(int i = 0; i <= n; i++){
for(int j = 0; j <= MaxP; j++){
dp[i][j] = UNF;
}
}
for(int i = 1; i <= w + 1; i++){
for(int j = 0; j <= min(MaxP, AS[i]); j++){
dp[i][j] = -AP[i] * j;
}
}
dp[0][0] = 0;
//
for(int i = 1; i <= n; i++){//枚举天数
for(int j = 0; j <= MaxP; j++){
dp[i][j] = max(dp[i][j], dp[i-1][j]);
}
if(i <= w + 1) continue;
head1 = tail1 = 0;
for(int j = 0; j <= MaxP; j++){
int nowf = dp[i - w - 1][j] + j * AP[i];
while(head1 < tail1 && que1[head1].pos + AS[i] < j) head1++;
while(head1 < tail1 && que1[tail1 - 1].f < nowf) tail1--;
que1[tail1].f = nowf, que1[tail1].pos = j; tail1++;
dp[i][j] = max(dp[i][j], que1[head1].f - j * AP[i]);
}
head1 = tail1 = 0;
for(int j = MaxP; j >= 0; j--){
int nowf = dp[i - w - 1][j] + j * BP[i];
while(head1 < tail1 && que1[head1].pos - BS[i] > j) head1++;
while(head1 < tail1 && que1[tail1 - 1].f < nowf) tail1--;
que1[tail1].f = nowf, que1[tail1].pos = j, tail1++;
dp[i][j] = max(dp[i][j], que1[head1].f - j * BP[i]);
}
}
//
int ans = 0;
for(int i = 0; i <= MaxP; i++){
ans = max(ans, dp[n][i]);
}
cout << ans << endl;
}
return 0;
}