Problem
Description
我们需要将一个文件复制到n个服务器上,这些服务器的编号为S1, S2, …, Sn。
首先,我们可以选择一些服务器,直接把文件复制到它们中;将文件复制到服务器Si上,需要花费ci > 0的置放费用。对于没有直接被复制文件的服务器Si来说,它依次向后检查Si+1, Si+2, …直到找到一台服务器Sj:Sj中的文件是通过直接复制得到的,于是Si从Sj处间接复制得到该文件,这种复制方式的读取费用是j – i(注意j>i)。另外,Sn中的文件必须是通过直接复制得到的,因为它不可能间接的通过别的服务器进行复制。我们设计一种复制方案,即对每一台服务器确定它是通过直接还是间接的方式进行复制(Sn只能通过直接方式),最终使每一台服务器都得到文件,且总花费最小。
Input
输入文件的第一行有一个整数n,表示服务器的数目。输入文件的第二行有n个整数,顺数第i个表示ci:在Si上直接复制文件的费用。
Output
输出文件中只包含一个整数,即最少需要花费的费用。
Sample Input
10
2 3 1 5 4 5 6 3 1 2
Sample Output
18
Data Constraint
60%的数据中,1 <= n <= 1 000
100%的数据中,1 <= n <= 1 000 000
80%的数据中, 1 <= ci <= 50
100%的数据中,1 <= ci <= 1 000 000 000
最终结果可能较大,请注意选择适当的数据类型进行计算。
Hint
Solution
向后往前递推
60分方法
设F[i]表示i点直接复制,且右边的所有点已处理完的最小费用。依题意得:
时间复杂度:O(n^2)
100分算法
方法:斜率优化
如果有两个点j,k,设j比k更优,则有
移项,整理,得
我们建立一个队列D,里面的数量关系如下:
d[1]>d[2]>d[3]>….
则我们要把大于i的xl(j,k)统统踢掉,然后将i放入队列。但是一定要保证i暂时是最小的,因此要将队尾的大于i的xl(j,k)统统踢掉。
那么问题来了,为什么要从n-1 downto 0?(看下面的code)因为有的情况是1号服务器不直接复制,且即使1号服务器直接复制,f[1]=f[0],因此用0代表第0号直接复制。因而递推到0,输出f[0].
我讲得不是很好,因为我还是新手,敌不过老司机,希望大家多多包涵。如果想看详细一点的题解,
链接: http://blog.csdn.net/lyd_7_29/article/details/51637686
Code
var a,f,d:array[0..1000001] of int64;
i,j,l,r,n:longint;
function min(a,b:int64):int64;
begin
if a<b then exit(a) else exit(b);
end;
function jj(n:int64):int64;
begin
exit(n*(n+1) div 2);
end;
function xl(j,k:int64):real;
begin
exit((2*f[j]-2*f[k]+sqr(j)-sqr(k)-j+k)/(2*(j-k)));
end;
begin
readln(n);
for i:=1 to n do read(a[i]);
f[n]:=a[n];
d[1]:=n;
l:=1;
r:=1;
for i:=n-1 downto 0 do
begin
while (l<r)and(i<xl(d[l],d[l+1])) do inc(l);
f[i]:=f[d[l]]+a[i]+jj(d[l]-i-1);
while (l<r)and(xl(d[r],i)>xl(d[r-1],d[r])) do dec(r);
inc(r);
d[r]:=i;
end;
writeln(f[0]);
end.
——2016.6.12