木板

《高級數據結構》p95 例3-4


【算法提點】


樸素想法

貪心的算法,每次插入木板使木板右端儘量靠左。

設每次插入木板後最右端位置爲pre

考慮每一塊木板右端的值=max(p[i],pre+l[i])

如果max=p[i]

|      -----.  (----爲木板   .爲釘子)

如果max=pre+l[i]

|--------.----   (----爲木板   .爲釘子)

每一次找最小的max,就是把所有未插入木板掃一遍,時間複雜度是N^2的



優化

對木板進行分類

對於第一類木板——每一塊max=p[i]的木板i,在pre向右變大的過程中,max有可能會變成pre+l[i],變成第二類木板

對於第二類木板——每一塊max=pre+l[i]的木板i,由於pre在不斷變大,p[i]不變,所以第二類木板的pre+l[i]恆大於p[i],所以原本就在第二類的木板和第一類變成第二類的木板不可能變回第一類

min(max(p[i],pre+l[i]))=min(第一類木板最小值(最小值的木板要能放,即p[i]>=pre),第二類木板最小值(最小值的木板要能放,即p[i]>=pre))

於是用堆來維護兩類木板,每類木板取最小值時都要判斷能不能放,若不能放則丟掉直到找到能放的或堆空;第一類木板在取最小值時如果發現pre+l[i]>p[i]就把它放到第二類中,直到取出p[i]>pre+l[i]!!!

!!!第一類取出滿足的一個木板後第一類中可能仍然有剩下的要變成第二類的木板,沒有必要本次操作對他們處理

              證明:對於仍在第一類堆中不是最小的但要變成第二類的木板,設該木板爲a(a也表示該木板最右端位置)

                        對於第一類堆中最小的(不要變成第二類的木板),設該木板爲b(b也表示該木板最右端位置)

                        滿足pre+l[a]>p[a] p[a]>p[b]

                        所以a>b

                        設第二堆中最小木板爲c(c也表示該木板最右端位置)

                        若a<c 因爲b<a 所以b一定min(第一類木板最小值,第二類木板最小值)

                        若a>c 則min(max(p[i],pre+l[i]))=(b,c)

                        所以a不影響結果,所以本次操作把不把它們變成第二類都沒事

重複放置操作,當第一類和第二類木板都空時,就結束放置。



優化後時間複雜度

這樣算法的時間複雜度是n*log2(n)最多n此操作,每一次操作維護兩個堆都是log2(n)的,其中第一類變成第二類的維護總複雜度最多爲n*log2(n)

【代碼】

代碼只過了樣例,其他數據沒有測試過!

//2016-4-9
//God save princess
//By Shui'Bing ICEE
const
  maxn=200000;
var
  n,pre,i,l1,l2,min1,min2,s:longint;
  l,p,heap1,heap2:array[0..maxn] of longint;
  procedure put1(x:longint);
var
  son,temp:longint;
begin
  inc(l1);
  heap1[l1]:=x;
  son:=l1;
  while (son<>1) and (p[heap1[son]]<p[heap1[son div 2]]) do
    begin
      temp:=heap1[son];
      heap1[son]:=heap1[son div 2];
      heap1[son div 2]:=temp;
      son:=son div 2;
    end;
end;

  procedure put2(x:longint);
var
  son,temp:longint;
begin
  inc(l2);
  heap2[l2]:=x;
  son:=l2;
  while (son<>1) and (l[heap2[son]]<l[heap2[son div 2]]) do
    begin
      temp:=heap2[son];
      heap2[son]:=heap2[son div 2];
      heap2[son div 2]:=temp;
      son:=son div 2;
    end;
end;

  procedure get1;
var
  fa,son,temp:longint;
begin
  heap1[1]:=heap1[l1];
  heap1[l1]:=0;
  dec(l1);
  fa:=1;
  while fa*2<=l1 do
    begin
      if (fa*2+1>l1) or (p[heap1[fa*2]]<p[heap1[fa*2+1]])
      then son:=fa*2
      else son:=fa*2+1;
      if p[heap1[fa]]>p[heap1[son]]
      then begin
             temp:=heap1[fa];
             heap1[fa]:=heap1[son];
             heap1[son]:=temp;
             fa:=son
           end
       else break;
    end;
end;

  procedure get2;
var
  fa,son,temp:longint;
begin
  heap2[1]:=heap2[l2];
  heap2[l2]:=0;
  dec(l2);
  fa:=1;
  while fa*2<=l2 do
    begin
      if (fa*2+1>l2) or (l[heap2[fa*2]]<l[heap2[fa*2+1]])
      then son:=fa*2
      else son:=fa*2+1;
      if l[heap2[fa]]>l[heap2[son]]
      then begin
             temp:=heap2[fa];
             heap2[fa]:=heap2[son];
             heap2[son]:=temp;
             fa:=son
           end
       else break;
    end;
end;

begin
  read(n);
  pre:=maxlongint;
  for i:=1 to n do
    begin
      read(l[i],p[i]);
      if p[i]<pre
      then pre:=p[i];
    end;
  l1:=0;
  l2:=0;
  for i:=1 to n do
    begin
      if p[i]<>pre
      then begin
             if pre+l[i]>=p[i]
             then put2(i)
             else put1(i);
           end;
    end;
  s:=1;
  while 1+1=2 do
    begin
      min1:=maxlongint;
      while l1>0 do
        begin
          if p[heap1[1]]<pre
          then begin
                 get1;
                 continue;
               end;
          if l[heap1[1]]+pre>=p[heap1[1]]
          then begin
                 put2(heap1[1]);
                 get1;
               end
          else begin
                 min1:=p[heap1[1]];
                 break;
               end;
        end;
      min2:=maxlongint;
      while l2>0 do
        begin
          if p[heap2[1]]<pre
          then begin
                 get2;
                 continue;
               end
          else begin
                 min2:=l[heap2[1]]+pre;
                 break;
               end;
        end;
      if (min1=maxlongint) and (min2=maxlongint)
      then break;
      inc(s);
      if min1<min2
      then begin
             pre:=min1;
             get1;
           end
      else if min1>min2
           then begin
                  pre:=min2;
                  get2;
                end
           else begin
                  pre:=min1;
                  if p[heap1[1]]<p[heap2[1]]
                  then get1
                  else get2;
                end;
    end;
  writeln(s);
end.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章