题目描述 :http://wenku.baidu.com/view/aa1c24160b4e767f5acfce54.html
题目大意是,在一条直线上有n个点,我们要从中取出k对点(k*2<=n),点不能被重复取,每对点之间有一个距离,求距离的最小和。
n不超过100000 k不超过 n div 2
容易证明,组成一对的两个点一定 是相邻的:
比如: A-------------------------B
C-----------------------D
如果AB一对,CD一对,那还不如AC一对,BD一对;
很明显,dp是o(nk)的 :F[ i,K]:=MIN(F[ i-1,K],F[ i-2,K-1]+W[ i-1,i ])
F[i,k]表示扫到第i-1到第i 这两个点,取了k对的最优值。
tle是明显的,可以考虑效率更高的贪心。
因为只能选相邻的点,我们不如用n-1 个距离代替n个点。
这样,每次贪心的选取最小距离。
但这明显是错的,比如:A------------------------B--------------C------D----------E------------------------F
1000 10 1 10 100
如果我们直接贪心的选取了CD,那么接下来就只能选AB或EF,这样还不如选BC 和 EF
所以要对贪心进行修正,比如,我们在选了CD之后,删除BC ,CD,DE,然后构造 BE=BC+DE-CD=19,并添加进去:
A------------------------B------------------E------------------------F
1000 19 1000
这样,当我们贪心选取BE的时候,就等于把CD“还了回来”,并选取了BC和DE两段,等于说还是多选了一段
这样,选一次就多选了一段,贪心选n次即可。
实现的时候,贪心取最小可以用堆(我的堆 写丑了,其实用zkw线段树更漂亮),删除和添加需要有链表辅助。
klog(n)的复杂度足够了。
program lmd;
var
pred,next,heap,link,dist:array[0..200000]of longint;
a:array[0..120000]of int64;
i,n,k,top,high:longint;
ans:int64;
procedure swap(x,y:longint);
var z:longint;
begin
link[heap[x]]:=y;
link[heap[y]]:=x;
z:=heap[x];heap[x]:=heap[y];heap[y]:=z;
end;
procedure up(k:longint);
begin
while (k<>1) and (a[heap[k shr 1]]>a[heap[k]]) do
begin
swap(k shr 1,k);
k:=k shr 1;
end;
end;
procedure down(k:longint);
var j:longint;
begin
while (heap[k shl 1]+heap[k shl 1+1]<>0) do
begin
j:=k shl 1;
if heap[j]=0 then j:=j+1;
if (a[heap[k shl 1+1]]<a[heap[j]]) and (heap[k shl 1+1]<>0) then
j:=k shl 1+1;
if a[heap[j]]<a[heap[k]] then
swap(k,j);
k:=j;
end;
end;
procedure add(where:longint);
begin
inc(top);
heap[top]:=where;
link[where]:=top;
up(top);
end;
procedure del(where:longint);
begin
where:=link[where];
link[heap[top]]:=where;
heap[where]:=heap[top];
down(where);
up(where);
heap[top]:=0;
dec(top);
end;
begin
assign(input,'Backup.in');
assign(output,'Backup.out');
reset(input);rewrite(output);
read(n,k);
dist[0]:=0;
for i:=1 to n do
read(dist[i]);
n:=n-1;
for i:=1 to n do
begin
a[i]:=dist[i+1]-dist[i];
add(i);
end;
for i:=1 to n do
begin
pred[i]:=i-1;
next[i]:=i+1;
end;
for i:=1 to k do
begin
high:=heap[1];
ans:=ans+a[high];
if (pred[high]<>0)and(next[high]<>n+1) then
begin
del(pred[high]);
del(next[high]);
del(high);
a[high]:=a[pred[high]]+a[next[high]]-a[high];
a[pred[high]]:=maxlongint shr 2;
a[next[high]]:=maxlongint shr 2;
add(high);
pred[high]:=pred[pred[high]];
next[pred[high]]:=high;
next[high]:=next[next[high]];
pred[next[high]]:=high;
end
else
begin
del(high);
if pred[high]=0 then
begin
del(next[high]);
pred[next[next[high]]]:=0;
end;
if next[high]=n+1 then
begin
del(pred[high]);
next[pred[pred[high]]]:=n+1;
end;
end;
end;
write(ans);
close(input);close(output);
end.