朴素方程:f[i,j]=min{f[k,j-1]+(tx[i]-tx[j])*(t[i]+j*s)} ①
非常经典和常用的优化就是根据每一次启动和分段对后面产生影响,可以优化为:
f[i]=min{f[k]+(t[i]-t[j]+s)*(tx[i]-tx[k])+s*(tx[n]-tx[i])} ②
而更进一步可以写成更方便的一个方程:
f[i]=min{f[k]+(tx[n]-tx[k])*(s+t[i]-t[k])} ③
这个似乎更能充分的应用对后面的影响这一特点。
根据③这个方程,我们可以进行更强大的优化:斜率优化。
为了更方便的解释这一优化,我们另tx[i]=∑tx[i~n],∴tx[n]-tx[i]=新的tx[i+1];
假设j<k<i,而且j优于k,那么:
f[j]+(t[i]-t[j]+s)*tx[j+1]<f[k]+(t[i]-t[k]+s)*tx[k+1];
化简:
f[j]+t[i]*tx[j+1]-t[j]*tx[j+1]+s*tx[j+1]<f[k]+t[i]*tx[k+1]-t[k]*tx[k+1]+s*tx[k+1];
移项:
f[j]-f[k]-t[j]*tx[j+1]+s*tx[j+1]+t[k]*tx[k+1]-s*tx[k+1]<t[i]*tx[k+1]-t[i]*tx[j+1];
进一步:
(f[j]-f[k]-t[j]*tx[j+1]+s*tx[j+1]+t[k]*tx[k+1]-s*tx[k+1])/(t[i]*tx[k+1]-t[i]*tx[j+1])>t[i];
∴ 可以进行优化
不妨自己写出代码:
var f,q,t,x,st,tx:array[0..10001]of int64; s,k:int64; n,head,tail,i:longint; function ki(j,k:longint):double; var x:double; begin x:=(f[j]-f[k]+(tx[j+1]-tx[k+1])*s+tx[k+1]*st[k]-tx[j+1]*st[j])/(tx[k+1]-tx[j+1]); exit(x); end; begin readln(n); readln(s); for i:=1 to n do begin readln(t[i],x[i]); st[i]:=st[i-1]+t[i]; end; tx[n+1]:=0; for i:=n downto 1 do tx[i]:=tx[i+1]+x[i]; f[0]:=0; q[1]:=0; head:=1; tail:=1; for i:=1 to n do begin while (head<tail)and(ki(q[head],q[head+1])<=st[i])do inc(head); k:=q[head]; f[i]:=f[k]+tx[k+1]*(s+st[i]-st[k]); while (head<tail)and(ki(q[tail-1],q[tail])>=ki(q[tail],i))do dec(tail); inc(tail); q[tail]:=i; end; writeln(f[n]); end.
(此代码poj上未通过,不过自己和别的程序对拍了n组极限数据都能过)