很明显的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; }