【贪心】合并果子{加强版的}

 

合并果子

【问题描述】

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
  每一次合并,多多可以把其中任意不超过k堆果子合并到一起,消耗的体力等于合并在一起的这些堆果子的重量之和。最终合并成为一堆果子。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

  例如有5堆果子,数目依次为3,2,1,4,5,每次合并最多3堆。可以先将1、2、3堆合并,新堆数目为6,耗费体力为6。接着,将新堆与剩下的两堆合并,又得到新的堆,数目为15,耗费体力为15。所以多多总共耗费体力=6+15=21。可以证明21为最小的体力耗费值。

【文件输入】

  输入包括两行,第一行是两个整数n和k(1<=n,k<=10000),表示果子的种类数和每次最多可以合并的堆数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。

【文件输出】

  输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231

【样例输入】

5 3
3 2 1 4 5 

【样例输出】

  21

【数据规模】

  对于30%的数据,保证有n<=1000:
  对于50%的数据,保证有n<=5000;
  对于全部的数据,保证有n<=10000。

============================

==============================

var
  n,k:longint;
  a:array[1..10000]of longint;
  dui:array[1..10000]of longint;
  dui_S:longint;
procedure init;
begin
  assign(input,'fruit.in');
  assign(output,'fruit.out');
  reset(input); rewrite(output);
end;

procedure terminate;
begin
  close(input); close(output);
  halt;
end;

procedure adjust(r:longint);
var
  k:longint;
  tem:longint;
begin
  k:=r shr 1;
  while (k>0)and(dui[k]>dui[r]) do
  //维护小根堆..
    begin
      tem:=dui[r]; dui[r]:=dui[k]; dui[k]:=tem;
      r:=k;
      k:=r shr 1;
    end;
end;

procedure shift(r,n:longint);
var
  k:longint;
  tem:longint;
begin
  k:=r shl 1;
  if (k+1<=n)and(dui[k+1]<dui[k]) then inc(k);
  while (k<=n)and(dui[r]>dui[k]) do
  //若根大于叶子节点则交换
    begin
      tem:=dui[r]; dui[r]:=dui[k]; dui[k]:=tem;
      r:=k;
      k:=r shl 1;
      if (k+1<=n)and(dui[k+1]<dui[k]) then inc(k);
    end;
end;

procedure main;
var
  i:longint;
  ans:longint;
  k1:longint;
  t:longint;
  nn:longint;
begin
  readln(n,k);
  nn:=n;

  dui_s:=0; ans:=0; //初始化
  for i:=1 to n do
    begin
      read(a[i]);
      inc(dui_s);
      dui[dui_s]:=a[i];
      adjust(dui_s);
      //入堆..并向上调整..
    end;
  if n<=k then
    begin
      ans:=0;
      for i:=1 to n do
        ans:=ans+a[i];
      writeln(ans);
      terminate;
    end;
  nn:=n;
  while nn>k do dec(nn,k-1);
  //nn:=n mod (k-1);
  t:=0;
  for i:=1 to nn do
    begin
      inc(t,dui[1]);
      dui[1]:=dui[dui_s];
      dec(dui_s);
      shift(1,dui_s);
    end;
  inc(dui_s);
  dui[dui_s]:=t;
  adjust(dui_s);
  ans:=t;
    
  while dui_s>1 do
   //若堆中元素>1则合并..
    begin
      k1:=k;
      t:=0;
      while (k1>0) do
        begin
          dec(k1);
          t:=t+dui[1];
          dui[1]:=dui[dui_s];
          dec(dui_s);
          shift(1,dui_s);
        end;//将堆中元素合并..
            //边界情况{1.合并达到k次;2.堆中没有元素了..;3.当前合成}
      inc(dui_s);
      dui[dui_s]:=t;
      adjust(dui_s);
      //将合并了的果子加入堆..并调整..
      ans:=ans+t;
      //累加代价.
    end;
  writeln(ans);
end;

begin
  init;
  main;
  terminate;
end.


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值