1. 程式人生 > >【貪心】合併果子{加強版的}

【貪心】合併果子{加強版的}

 

合併果子

【問題描述】

在一個果園裡,多多已經將所有的果子打了下來,而且按果子的不同種類分成了不同的堆。多多決定把所有的果子合成一堆。
  每一次合併,多多可以把其中任意不超過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.