APIO2007 数据备份(贪心)

        题目描述 :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.                    


      

            

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值