让我们继续练习dp
首先这道题约束条件很多
但实际上方程还是很好写的,f[i,j]表示第i天时拥有j只股票的最大收益
令p=max(0,i-k-1) 上一次较交易
易得f[i,j]=max(f[i-1,j],f[p,j-b]-ap[i]*b,f[p,j+s]+bp[i]*s) b<=as[i],s<=bs[i];
显然会TLE,我们要优化
f[i-1,j]我们可以先不管他,
我们令j1=j-b j2=j+s
则max(0,j-as[i])<=j1<=j
j<=j2<=min(m,j+bs[i]);
则原式可化为
f[i,j]=max(f[p,j1]+ap[i]*j1-ap[i]*j,f[p,j2]+bp[i]*j2-bp[i]*j)
观察得知对于当前的状态,结果只与j1,j2有关系
于是我们可以分开来对j1,j2求区间最大,再求一个总的最大就行
对此我们可以用线段树
但是,线段树算法O(n^2logn)而且实际常数较大,会TLE(一开始我就是这样)
观察区间其实总是整体右移的,这很像我一开始做的单调队列的滚动窗口那道题
于是我们可以用单调队列优化
加入只考虑买入的情况对于k1<k2
如果有f[p,k2]+ap[i]*k2>=f[p,k2]+ap[i]*k2 那么k2一定比k1优(更可能成为区间最大)
卖出情况同理,因此我们从0~m遍历一边,维护一个单调减的队列就行了
每个点最多出队一次,入队一次
因此复杂度为O(n^2);
1 const inf=-100000007; 2 var q,b,s,ns,nb,vs,vb:array[0..2010] of longint; 3 f:array[0..2010,0..2010] of longint; 4 l,ans,h,t,p,i,j,k,n,m:longint; 5 6 function max(a,b:longint):longint; 7 begin 8 if a>b then exit(a) else exit(b); 9 end; 10 11 function compareb(x,y:longint):boolean; 12 begin 13 if f[p,x]+vb[i]*x>=f[p,y]+vb[i]*y then exit(true) else exit(false); 14 end; 15 16 function compares(x,y:longint):boolean; 17 begin 18 if f[p,x]+vs[i]*x>=f[p,y]+vs[i]*y then exit(true) else exit(false); 19 end; 20 21 begin 22 readln(n,m,k); 23 for i:=1 to n do 24 readln(vb[i],vs[i],nb[i],ns[i]); 25 for i:=1 to m do 26 f[0,i]:=inf; 27 f[0,0]:=0; 28 for i:=1 to n do 29 begin 30 p:=max(i-k-1,0); 31 h:=0; 32 t:=0; 33 q[0]:=0; 34 for j:=0 to m do //买入情况 35 begin 36 if j<>0 then 37 begin 38 while (h<t) and compareb(j,q[t]) do dec(t); 39 inc(t); 40 q[t]:=j; 41 end; 42 l:=max(0,j-nb[i]); 43 while (q[h]<l) do inc(h); 44 while (h<t) and compareb(q[h+1],q[h]) do inc(h); 45 b[j]:=f[p,q[h]]+vb[i]*q[h]; 46 end; 47 h:=0; 48 t:=0; 49 q[0]:=m; 50 for j:=m downto 0 do //卖出情况 51 begin 52 if j<>m then 53 begin 54 while (h<t) and compares(j,q[t]) do dec(t); 55 inc(t); 56 q[t]:=j; 57 end; 58 l:=j+ns[i]; 59 if l>m then l:=m; 60 while (q[h]>l) do inc(h); 61 while (h<t) and compares(q[h+1],q[h]) do inc(h); 62 s[j]:=f[p,q[h]]+vs[i]*q[h]; 63 end; 64 for j:=0 to m do //求总的最大 65 f[i,j]:=max(f[i-1,j],max(b[j]-vb[i]*j,s[j]-vs[i]*j)); 66 end; 67 ans:=f[n,0]; //显然手上无股票最合算 68 writeln(ans); 69 end.