很明显的DP,不过省选主要考察的就是DP优化了。
朴素的做法如下:
状态:用f[i][j]表示前i天中,最后1天收盘时手中还持有j股的股票所能得到的最大收益。
转移方程:f[i][j] = max{f[i - 1][j],f[i - W - 1][j - k1] - ap[i] * k1,f[i - W - 1][j + k2] + bp[i] * k2}。
其中,k1 <= as[i], k2 <= bs[i]。
以上算法的复杂度是O(T * maxP ^ 2),显然过不了极限数据。
对于以上转移方程方程,可做如下转化:
令t = i - W - 1,p = j - k1, q = j + k2。
忽略f[i - 1][j]项,则有:
f[i][j] = max{f[t][p] - ap[i] * (j - p),f[t][q] + bp[i] * (q - j)},
且p >= j - as[i], q <= j + bs[i]。
即f[i][j] = max{f[t][p] + ap[i] * p - ap[i] * j, (1)
f[t][q] + bp[i] * q - bp[i] * j}。 (2)
这样一来,可以发现(1)式中,f[t][p] + ap[i] * p部分,和(2)式中f[t][q] + bq[i] * q部分只与p, q有关,于是可以使用单调队列优化。(以上转自Whjpji)
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 2000 + 10;
struct day
{
int ap,bp,as,bs;
}deal[maxn];
int f[maxn][maxn];
int q[maxn],val[maxn];
int t,maxp,w;
int l,r;
int ans = 0;
void init()
{
freopen("bzoj1855.in","r",stdin);
freopen("bzoj1855.out","w",stdout);
}
int max(int a,int b)
{
return a > b ? a : b;
}
void readdata()
{
scanf("%d%d%d",&t,&maxp,&w);
for(int i = 1;i <= t;i++)
{
scanf("%d%d%d%d",&deal[i].ap,&deal[i].bp,&deal[i].as,&deal[i].bs);
}
}
void solve()
{
memset(f,~0x3f,sizeof(f));
f[0][0] = 0;
for(int i = 1;i <= t;i++)
{
for(int j = 0;j <= deal[i].as;j++)f[i][j] = -deal[i].ap * j;
for(int j = 0;j <= maxp;j++)
f[i][j] = max(f[i][j],f[i-1][j]);
int t = i - w - 1;
if(t >= 0)
{
l = r = 0;
for(int j = 0;j <= maxp;j++)
{
while(l < r && q[l] < j - deal[i].as)++l;
while(l < r && f[t][j] + j * deal[i].ap >= val[r-1])--r;
val[r] = f[t][j] + j * deal[i].ap;
q[r++] = j;
if(l < r)f[i][j] = max(f[i][j],val[l] - j * deal[i].ap);
}
l = r = 0;
for(int j = maxp;j >= 0;j--)
{
while(l < r && q[l] > j + deal[i].bs)++l;
while(l < r && f[t][j] + j * deal[i].bp >= val[r-1])--r;
val[r] = f[t][j] + j * deal[i].bp;
q[r++] = j;
if(l < r)f[i][j] = max(f[i][j],val[l] - j * deal[i].bp);
}
}
ans = max(f[i][0],ans);
}
printf("%d",ans);
}
int main()
{
init();
readdata();
solve();
return 0;
}