让我们继续练习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.
View Code

 

posted on 2014-05-15 19:15  acphile  阅读(205)  评论(0编辑  收藏  举报