1. 程式人生 > >堆的應用:地鼠遊戲

堆的應用:地鼠遊戲

地鼠遊戲

題目描述 Description

王鋼是一名學習成績優異的學生,在平時的學習中,他總能利用一切時間認真高效地學習,他不但學習刻苦,而且善於經常總結、完善自己的學習方法,所以他總能在每次考試中得到優異的分數,這一切很大程度上是由於他是一個追求效率的人。

但王鋼也是一個喜歡玩的人,平時在學校學習他努力剋制自己玩,可在星期天他卻會抽一定的時間讓自己玩一下,他的爸爸媽媽也比較信任他的學習能力和學習習慣,所以在星期天也不會象其他家長一樣對他抓緊,而是允許他在星期天上午可以自由支配時間。

地鼠遊戲是一項需要反應速度和敏捷判斷力的遊戲。遊戲開始時,會在地板上一下子冒出很多地鼠來,然後等你用榔頭去敲擊這些地鼠,每個地鼠被敲擊後,將會增加相應的遊戲分值。問題是這些地鼠不會傻傻地等你去敲擊,它總會在冒出一會時間後又鑽到地板下面去(而且再也不上來),每個地鼠冒出後停留的時間可能是不同的,而且每個地鼠被敲擊後增加的遊戲分值也可能是不同,為了勝出,遊戲參與者就必須根據每個地鼠的特性,有選擇地儘快敲擊一些地鼠,使得總的得分最大。

這個極具挑戰性的遊戲王鋼特別喜歡,最近他經常在星期天上午玩這個遊戲,慢慢地他不但敲擊速度越來越快(敲擊每個地鼠所需要的耗時是1秒),而且他還發現了遊戲的一些特徵,那就是每次遊戲重新開始後,某個地鼠冒出來後停留的時間都是固定的,而且他記錄了每個地鼠被敲擊後將會增加的分值。於是,他在每次遊戲開始後總能有次序地選擇敲擊不同的地鼠,保證每次得到最大的總分值。

輸入描述 Input Description

輸入包含3行,第一行包含一個整數n(1<=n<=100)表示有n個地鼠從地上冒出來,第二行n個用空格分隔的整數表示每個地鼠冒出後停留的時間,第三行n個用空格分隔的整數表示每個地鼠被敲擊後會增加的分值(<=100)。每行中第i個數都表示第i個地鼠的資訊。

輸出描述 Output Description

輸出只有一行一個整數,表示王鋼所能獲得的最大遊戲總分值。

樣例輸入 Sample Input

5

5 3 6 1 4

7 9 2 1 5

樣例輸出 Sample Output

24

這題我記得是安徽一道省選題,但資料比這凶猛多了,貌似是 n<=200000

然而這題我當年又是騙分的……(眾:這麼水寫個樸素的也好啊!)然而誰叫我就是這麼水呢……

我當時是把所有地鼠分加起來直接輸出……騙了40分……( ╯□╰ )

再來說說正解吧。

首先貪心是不難想到的。但是這裡又有一個問題:如果我們按時間順序貪,有可能會有這種情況:一個地鼠為99分,只停留1秒,而另一個地鼠停留10秒,為100分。

這時如果我們按貪心來,會選100分的,然而有可能2~9秒全是1,2分的地鼠,這時策略顯然不對。

然後思路來了:我們從後往前貪心,因為每一秒不打白不打,所以就選當前分最大的打,這樣一定最優。

但是對於這題的小資料可以直接貪或DP,對於200000的規模呢?

談到貪心的優化,很多時候是用堆的。

所以我們用堆來優化:每次把當前能打的地鼠彈進一個大根堆,每次打就選根節點的地鼠,把它出堆,再維護一下堆即可。

program heap;
var 
    n,t,i,j,ans,len:longint;

    time,value,a:array[-1..10000] of longint;

procedure sort;
var 
   i,j,p:longint;
begin 

   for i:=1 to n-1 do 
       for j:=i+1 to n do 
           if time[i]>time[j] then begin 
            p:=time[i];time[i]:=time[j];time[j]:=p;
            p:=value[i];value[i]:=value[j];value[j]:=p;
           end;

end;

procedure inser(k:longint);
var 
    dad,p:longint;
begin 

    inc(len);

    a[len]:=value[k];

    k:=len;

    repeat 

       dad:=k div 2;

       if (dad>0)and(a[dad]<a[k]) then begin 

          p:=a[dad];a[dad]:=a[k];a[k]:=p;

          k:=dad;

       end
       else 
           break;

    until false;

end;

procedure heapify(k:longint);
var 
    l,r,max,p:longint;
begin 

    repeat 

       l:=k*2;

       r:=k*2+1;

       max:=k;

       if (l<=len)and(a[l]>a[max]) then max:=l;

       if (r<=len)and(a[r]>a[max]) then max:=r;

       if k<>max then begin 

          p:=a[max];a[max]:=a[k];a[k]:=p;

          k:=max;

       end
       else 
           break;

    until false;

end;

procedure solve;
var 
   p:longint;
begin 

    if len=0 then exit;

    p:=a[1];a[1]:=a[len];a[len]:=p;

    ans:=ans+a[len];

    dec(len);

    heapify(1); //維護堆

end;

begin 

    read(n);

    t:=0; len:=0;

    fillchar(a,sizeof(a),0);


    for i:=1 to n do begin  
        read(time[i]);

        if time[i]>t then t:=time[i];
    end;

    for i:=1 to n do 
        read(value[i]);

    sort;//按時間排序

    j:=n;

    ans:=0;

    for i:=t downto 1 do begin //從後往前貪

        while (time[j]=i)and(j>0) do begin 

           inser(j); //入堆

           dec(j);

        end;

        solve; //打地鼠

    end;

    writeln(ans);

end.