题目链接
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 \times 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) \times 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) \times 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) \times AP_i=f[i - w - 1][k] + k \times AP_i-j \times AP_i\)
\(f[i - w - 1][k]+(k-j) \times BP_i=f[i - w - 1][k] + k \times BP_i-j \times BP_i\)
后面和状态有关的常数项暂时忽略,首先,对于一个状态\(f[i][j]\),它的决策点的\(i\)是固定为\(i-w-1\)的,然后我们看\(k\)的取值范围,如果忽略掉上下界的话就是滑动窗口问题,于是我们可以对于每个\(i\)预处理出当前时刻\(i\)所有\(j\)的最优决策
复杂度\(O(n^2)\)
#include
#include
#include
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\n",ans);
return 0;
}