Solution [SCOI2010]股票交易
题目大意:给定(n)天,每一天有股票买入价格(AP_i),买入限制(AS_i),卖出价格(BP_i),卖出限制(BS_i),每次交易后需间隔至少(w)天(含(w)),手中股票数量不得大于(MaxP),求最大收益
动态规划,单调队列
分析:我们很快就可以列出一个(O(n^3))的朴素(dp)
设(f[i][j])表示到(i)天为止,在第(i)天时手中有(j)张股票的最大收益,然后分类讨论
- (1.)无中生有(
友),之前啥都不干然后在(i)天购买股票:
(f[i][j] = max(f[i][j],-j imes AP_i) quad j in [0,AS_i])
- (2.)啥都不干
(f[i][j] = max(f[i][j],f[i-1][j]))
- (3.)买股票
(f[i][j] = max(f[i][j],f[i - w - 1][k]-(j-k) imes AP_i) quad i > w,k in [max(0,j - AS_i),j])
- (4.)卖股票
(f[i][j] = max(f[i][j],f[i - w - 1][k]+(k-j) imes BP_i) quad i > w,k in[j,min(MaxP,j+BS_i)])
我们买卖股票的时候不需要枚举最优最测点的(i)(也就是天数),因为它们都被(i-w-1)天计算过了
然后状态(n^2)多半没法继续优化了,我们考虑从(O(n))转移下手
我们先拆式子,把和决策点相关的部分和跟状态相关的部分分开
(f[i - w - 1][k]-(j-k) imes AP_i=f[i - w - 1][k] + k imes AP_i-j imes AP_i)
(f[i - w - 1][k]+(k-j) imes BP_i=f[i - w - 1][k] + k imes BP_i-j imes BP_i)
后面和状态有关的常数项暂时忽略,首先,对于一个状态(f[i][j]),它的决策点的(i)是固定为(i-w-1)的,然后我们看(k)的取值范围,如果忽略掉上下界的话就是滑动窗口问题,于是我们可以对于每个(i)预处理出当前时刻(i)所有(j)的最优决策
复杂度(O(n^2))
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int maxn = 2048;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
int val[2][maxn],q[maxn],ans[2][maxn],f[maxn][maxn],AP[maxn],AS[maxn],BP[maxn],BS[maxn],n,maxp,w,head,tail;
inline void solve(int tim){
for(int k = 0;k <= maxp;k++)
val[0][k + 1] = f[tim - w - 1][k] + k * AP[tim],val[1][k + 1] = f[tim - w - 1][k] + k * BP[tim],ans[0][k] = ans[1][k] = -0x7fffffff;
head = 1,tail = q[0] = 0;
for(int i = 1;i <= maxp + 1;i++){
while(head <= tail && val[0][q[tail]] <= val[0][i])tail--;
q[++tail] = i;
while(head <= tail && q[head] < i - AS[tim])head++;
if(head <= tail)ans[0][i - 1] = val[0][q[head]];
}
head = 1,tail = q[0] = 0;
for(int i = maxp + 1;i >= 1;i--){
while(head <= tail && val[1][q[tail]] <= val[1][i])tail--;
q[++tail] = i;
while(head <= tail && q[head] > i + BS[tim])head++;
if(head <= tail)ans[1][i - 1] = val[1][q[head]];
}
}
int main(){
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);
for(int i = 1;i <= maxp;i++)f[0][i] = -0x3f3f3f3f;//这里注意,避免不合法状态
for(int i = 1;i <= n;i++){
if(i > w)solve(i);
for(int j = 0;j <= maxp;j++){
if(j <= AS[i])f[i][j] = -j * AP[i];
else f[i][j] = -0x3f3f3f3f;
f[i][j] = max(f[i][j],f[i - 1][j]);
if(i > w)f[i][j] = max(f[i][j],ans[0][j] - j * AP[i]);
if(i > w)f[i][j] = max(f[i][j],ans[1][j] - j * BP[i]);
}
}
int ans = 0;
for(int i = 0;i <= maxp;i++)ans = max(ans,f[n][i]);
printf("%d
",ans);
return 0;
}