Trade
题意:
炒股,给出第1~t天每天的买入价格Ap[i]和卖出价格Bp[i],每天最多能买入的数量As[i]和最多能卖出的数量Bs[i]。
还有几个限制,任意时刻最多持有Pmax数量的股票,两次交易(买入或卖出)间隔至少w天,如第i天交易,那么下次至少是i+w+1天。问你第t天最大收入。
题解:
首先考虑朴素DP。
令
DP[i][j]
表示第i天持有j股票时的最大收入。
讨论转移方程:
第i天啥也不干:
DP[i][j]=DP[i−1][j]
第i天买点,显然有
j>k
:
DP[i][j]=DP[i−w−1][k]−(j−k)∗Ap[i],(j−k<=As[i])
第i天卖点,显然有
k>j
:
DP[i][j]=DP[i−w−1][k]+(k−j)∗Bp[i],(k−j<=Bs[i])
然后每天能干的事就考虑完了,接下来考虑代码实现。
for(int i = 1; i <= t; ++i){
for(int j = 0; j <= p; ++j){
int mx = -inf;
mx = max(dp[i][j], dp[i-1][j]);
if(i <= w+1) continue;
for(int k = j; k >= 0 && j-k <= As[i]; --k){
mx = max(mx, dp[i-w-1][k]-(j-k)*Ap[i]);
}
for(int k = j; k <= p && k-j <= Bs[i]; ++k){
mx = max(mx, dp[i-w-1][k]+(k-j)*Bp[i]);
}
dp[i][j] = mx;
}
}
我写出来是这样的,这是个
t∗p∗p
的
DP
,显然过不了。
那就要想优化了。
第一个方程是
O(1)
的就不说了。
看第二个方程。
DP[i][j]=DP[i−w−1][k]−(j−k)∗Ap[i]
整理一下。
DP[i][j]=DP[i−w−1][k]+k∗Ap[i]−j∗Ap[i]
再令
f[i−w−1][k]=DP[i−w−1][k]+k∗Ap[i]
那么方程可以这样转化:
DP[i][j]=f[i−w−1][k]−j∗Ap[i]
那么转移完的结果就是
DP[i][j]=max(f[i−w−1][k])−j∗Ap[i],(j>k)
单调队列就是针对这样的方程进行优化的。
一般的,对于类似
DP[i]=max/min(f[k])+C[j]
,其中
C与k无关
,都可以使用单调队列,效果是使
O(N)
的转移降至
O(1)
。
代码实现上,根据维护的信息范围不同,遍历的顺序就不同。
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int inf = ~0u>>2;
const int N = 2005;
int dp[N][N]; // 第i天拥有j股票情况下的最大收入
// dp[i][j] = dp[i-1][j];
// dp[i][j] = dp[i-w-1][k]+k*Api-j*Api; j <= p && j-k <= Asi 买入
// dp[i][j] = dp[i-w-1][k]+k*Bpi-j*Bpi; j >= 0 k-j <= Bsi 卖出
//
// 令 f[i-w-1][k] = dp[i-w-1][k]+k*Api
// dp[i][j] = max(f[i-w-1][k]) - j*Api; j > k
//
// 令f[i-w-1][k] = dp[i-w-1][k]+k*Bpi
// dp[i][j] = max(f[i-w-1][k]) - j*Bpi j < k
int As[N], Bs[N], Ap[N], Bp[N];
int top, tail;
struct node{
int val, pos;
node(){}
node(int a, int b){ val = a, pos = b;}
}q[N*10];
int main(){
int T;
scanf("%d", &T);
while(T--){
int t, p, w;
scanf("%d%d%d", &t, &p, &w);
for(int i = 1; i <= t; ++i) scanf("%d%d%d%d", Ap+i, Bp+i, As+i, Bs+i);
for(int i = 0; i <= t; ++i) for(int j = 0; j <= p; ++j) dp[i][j] = -inf;
for(int i = 1; i <= w+1; ++i){
int up = min(p, As[i]);
for(int j = 0; j <= up; ++j){
dp[i][j] = -j*Ap[i];
}
}
for(int i = 1; i <= t; ++i){
top = tail = 0;
for(int j = 0; j <= p; ++j){
dp[i][j] = max(dp[i][j], dp[i-1][j]);
if(i <= w+1) continue;
int nf = dp[i-w-1][j]+j*Ap[i];
while(top < tail && q[tail-1].val < nf) tail--;
q[tail++] = node(nf, j);
while(top < tail && j-q[top].pos > As[i]) top++;
dp[i][j] = max(dp[i][j], q[top].val-j*Ap[i]);
}
top = tail = 0;
for(int j = p; j >= 0; --j){
dp[i][j] = max(dp[i][j], dp[i-1][j]);
if(i <= w+1) break;
int nf = dp[i-w-1][j]+j*Bp[i];
while(top < tail && q[tail-1].val < nf) tail--;
q[tail++] = node(nf, j);
while(top < tail && q[top].pos-j > Bs[i]) top++;
dp[i][j] = max(dp[i][j], q[top].val-j*Bp[i]);
}
}
int ans = 0;
for(int i = 0; i <= p; ++i) ans = max(ans, dp[t][i]);
printf("%d\n", ans);
}
}