[SCOI2010] 股票交易
[SCOI2010] 股票交易
单调队列优化 dp 的经典问题。
分析
dp 加单调队列优化。
首先,不难想到是动态规划,看数据范围,可以支持 \(O(n^2)\) 的算法。
第一维肯定是天数,但是显然不够,所以第二维可以是手中的股票数。设 \(f_{i,j}\) 为到第 \(i\) 天为止,手中已有 \(j\) 支股票能赚到的最多的钱。
初始状态,手中并没有任何股票,在某一天 \(i\) 买了 \(j\) 支股票,所以:
\[ f_{i,j}=-ap_i*j
\]
\[ (0 \le j \le as_i)
\]
然后每天只有三种情况,什么都不做,只买,只卖。
- 什么都不做,就是和上一天的状态一样,所以:
\[ f_{i,j}=\max{(f_{i,j},f_{i-1,j})}
\]
- 只买,因为要间隔至少 \(w\) 天,所以可以从 \([0,i-w-1]\) 天转移过来,但是因为上一种情况已经将 \([0,i-w-2]\) 的状态都能转移到第 \(i-w-1\) 天,设第 \(i-w-1\) 天手中有 \(k\) 支股票。因为要买入 \(j-k\) 支股票,再考虑限制,所以 \(0 < j-k \le as_i\),所以:
\[ f_{i,j}=\max{(f_{i,j},f_{i-w-1,k}-(j-k) \times ap_i)}
\]
\[ f_{i,j}=\max{(f_{i,j},(f_{i-w-1,k}+k \times ap_i)-j \times ap_i)}
\]
\[ (j-as_i \le k \le j-1)
\]
- 只卖,和上一种情况差不多,要卖出 \(k-j\) 支股票,考虑限制 \(0 < k-j \le bs_i\),所以:
\[ f_{i,j}=\max{(f_{i,j},f_{i-w-1,k}+(k-j) \times bp_i)}
\]
\[ f_{i,j}=\max{(f_{i,j},(f_{i-w-1,k}+k \times bp_i)-j \times bp_i)}
\]
\[ (j+1 \le k \le j+bs_i)
\]
因为 \(k\) 的区间是固定的,可以用单调队列优化掉枚举 \(k\)。
考虑一下边界状况:
以情况 \(2\) 为例,考虑 \(j\) 的范围为 \([0,MAXP]\),所以 \(k\) 的范围为 \([j-as_i,j-1]\),全局范围为 \([0,MAXP-1]\),考虑移动 \(k\) 指针,只要 \(k < j-1\) 就能一直移动 \(k\),所以加入队尾,而队头小于 \(j-as_i\) 说明过期要删去,队内单调递减。
在情况 \(3\) 中,\(k\) 的范围为 \([j+1,j+bs_i]\),全局范围为 \([1,MAXP]\),只要 \(k<j+bs_i\) 且 \(k<MAXP\) 时加入队尾,队头小于 \(j+1\) 时说明过期要删去,队内单调递减。
code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=2005;
int n,m,w;
int f[N][N],q[N];
void solve()
{
cin>>n>>m>>w;
memset(f,-0x3f,sizeof f);
for(int i=1;i<=n;i++)
{
int ap,bp,as,bs;
cin>>ap>>bp>>as>>bs;
for(int j=0;j<=as;j++) f[i][j]=-ap*j;
for(int j=0;j<=m;j++) f[i][j]=max(f[i][j],f[i-1][j]);
if(i<=w) continue;
int l=1,r=0,k=-1;
for(int j=0;j<=m;j++)
{
while(k<j-1)
{
k++;
while(l<=r&&f[i-w-1][q[r]]+q[r]*ap<=f[i-w-1][k]+k*ap) r--;
q[++r]=k;
}
while(l<=r&&q[l]<j-as) l++;
if(l<=r) f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*ap-j*ap);
}
l=1,r=0,k=0;
for(int j=0;j<=m;j++)
{
while(k<j+bs&&k<m)
{
k++;
while(l<=r&&f[i-w-1][q[r]]+q[r]*bp<=f[i-w-1][k]+k*bp) r--;
q[++r]=k;
}
while(l<=r&&q[l]<j+1) l++;
if(l<=r) f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*bp-j*bp);
}
}
int ans=0;
for(int i=0;i<=m;i++) ans=max(ans,f[n][i]);
cout<<ans<<'\n';
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}