NDK 1368 yep的收藏品

問題描述:

 yep有很多收藏品,爲的是以後有機會送給它鍾愛的女性朋友,每到假期它都會去收集各式  各樣的收藏品,面對琳琅滿目的收藏品有時候它都不知道如何下手。但爲了女生它一般還是  會勇於獻出自己的錢包……

 又到了七夕,yep又該開始準備禮物了,禮物當然是從它所有的收藏品中選取,它有很多很  多件收藏品,但其中不乏重複的收藏,女生收到禮物時是不喜歡看見同種禮物的,要不然會  被視爲湊數,被發現的後果很嚴重,yep會被狠狠地X掉。yep的收藏品是以其價值度來區分  的,即同種的收藏的價值度相同,不同的收藏品價值度不同。價值度數字越小表明這個收藏  品越好。

 但是由於yep的懶惰,買好後的收藏品它從沒整理過,於是它們都堆在了一個箱子裏面,每  次從箱子中只能拿出一件收藏品,而且拿出一件收藏品必須要把堆在它上面的所有收藏品都  搗出來,所有的收藏品都按先後順序一個一個壘在箱子裏面。

 可沒等到七夕那天,女生竟然提前拜訪!當時yep還沒整理出它要送的禮物呢…………當女  生看到那個箱子的時候,她不禁皺起了眉頭,她說:“爲了懲罰你這個懶蛋,我要對你送我  的禮物再提出要求。首先,你要送我一個收藏品,你必須也要送給我這件收藏品底下緊挨着  的收藏品,除非它是最底下的一個。否則就不能再送我禮物了,因爲你只有一次機會;其  次,你要送給我價值度爲I的收藏就一定要送給我價值爲I-1的收藏品;最後,你必須送給我  儘可能多的收藏品。“

 面對這個情形,yep心急如焚。幸好它平時有記錄自己都買了哪些收藏品,根據記錄它就可  以選擇要送給她的收藏了……

 現在給你它的記錄,請你幫它挑選……

數據輸入:

 本題目每個測試點有T組數據。

 第一行輸入一個T,表示數據組數,對於每組數據:

 接下來T組數據:

 第一行一個整數n,表示yep擁有的收藏品個數;

 第二行n個數,按順序表示yep的每件收藏品的價值度,第i個讀入的收藏品的位置編號爲i。  (價值度均不大於N)

結果輸出:

 每組數據只有一行。

 如果有解,輸出兩個數max和ans,用一個空格隔開。

 max表示yep送給女生的收藏品個數;ans表示它送給她的第一件收藏最早的可能的位置編  號。

 如果無解,輸出一個0即可。

樣例:

 6

 4

 4 3 2 1

 4

 2 1 2 3

 4

 3 3 2 1

 4

 2 1 1 3

 4

 3 2 4 1

 4

 2 1 4 2

4 1

3 2

3 2

2 1

4 1

2 1 

核心思想:

 經過簡化之後本題的意思是:在一串數中找出連續的一段K個數,它們恰好組成1~K的一個  排列,輸出最大的K以及這個數段第一個數的位置(如果有多個段符合條件,輸出最靠前  的)。

 數據的範圍爲100000,考慮有O(nlogn)或O(n)兩種算法時間複雜度。這裏採用的是基本  O(n)的算法。

 由於數段要符合1~K的排列,所以不可能出現兩個一樣的數。用數組b[I]記錄當前數字I所在  的位置,即a[b[I]]=I。用一個變量start表示當前數段的第一個數的位置,初始賦成1。從第一  個數開始掃描,當遇到一個以前已經出現的數,即b[a[I]]<>0。此時再繼續掃描已經沒有意  義,因爲已經有重複的數了,必須先處理原來的數,更新最優解,之後刪除原來b[a[I]]及之  前的所有數,因爲更新最優解後b[a[I]]之前的數(包括它自己)都沒有用了。之後把start更  新爲b[a[I]]+1(這個b[a[I]]還是原來的數,並沒有更新),之後再把b[a[I]]的值更新爲I。依次  枚舉直到全部掃描完,最後再更新一次。

 之後的問題是如何更新最優解了,用l、r表示當前數段的左座標和右座標。首先如果b[1]=0  表示當前數段內沒有1,肯定無解;其次,要保證K=r-l+1。於是可以枚舉K,數段長度最少  是2,最長是I(當時第一層循環枚舉到的數)-start。直到b[K]=0,或者退出循環,如果b[K]在  區間[l,r]之外則更新[l,r],如果K=r-l+1那麼表示此時[l,r]之間恰好是1~r-l+1的排列,更新最  優解,並記錄數段起始位置。

 此算法枚舉數時間複雜度O(n),刪除數複雜度O(n),不確定因素唯有第二層循環枚舉K的  量,只能因數據而異,但實際上由於如果b[a[I]]=0那麼直接更新b[a[I]]而不會進入循環,頻繁  進入循環勢必不會枚舉過多量,枚舉很多量要建立在之前很長時間沒有進入第二層的循環基  礎之上,所以複雜度不會過高,但不排除有使本算法退化成O(n^2)的數據,限於本人數學水  平有限,不會進行嚴謹的證明,時間分析姑且寫到這裏。

 空間複雜度O(n)

 題解這麼強大絕對不是我寫的,而且程序會更強大,各位扶好坐好。

{
  The problem is to find a number K that you can find K collections in a row
  which they are exactly a permutation of 1..K.
}
program yepcollection;  //Written by UCHIHA.TMTK.INU  at 2009.4.1 0:00
var
  a:array[0..200001]of longint; //a[i] means the ith collection.
  b:array[0..200001]of longint; //b[i] means the current number of the collection which the value is i.
  i,t:longint;


procedure work;
var
  i,j,n:longint;
  ans,max:longint; //ans,max are what the problem description said.
  start,temp:longint; //start means the current line's first number and the collections before it are useless;temp means temp.....


  procedure update(h:longint);//to update the ans and the max
  var
    i:longint;
    l,r:longint;
    now:longint;//the current legal max value
  begin

    l:=b[1]; r:=b[1];     //l,r means the left edge and the right edge number
    now:=1;
    if l=0 then exit;     //if b[1]=0 then exit...because the most valuable collection is needed.
    if (now=(r-l+1))and((now>max)or((now=max)and(ans>l)))then begin
      max:=now;
      ans:=l;
    end;

    for i:=2 to h-start do begin //to find the max,and the max is only can be chosen from 2 to h-start

      if b[i]=0 then break;
      inc(now);
      if r<b[i] then r:=b[i]
        else if b[i]<l then l:=b[i];

      if (now=(r-l+1))and((now>max)or((now=max)and(ans>l)))then begin
        max:=now;
        ans:=l;
      end;

    end;

  end;


begin

  start:=1;
  max:=0; ans:=0;
  fillchar(a,sizeof(a),0);
  fillchar(b,sizeof(b),0);
  readln(n);
  for i:=1 to n do begin

    read(a[i]);
    if b[a[i]]=0 then b[a[i]]:=i
      else begin  //Because every value we can only have one collection,when we find two,we should deal with them.

        update(i);
        temp:=b[a[i]];
        for j:=start to b[a[i]] do b[a[j]]:=0;
        start:=temp+1;
        b[a[i]]:=i;

      end;

  end;
  update(n+1);
  if max<>0 then writeln(max,' ',ans) else writeln(0);

end;


begin
  assign(input,'yepcollection.in');
  reset(input);
  assign(output,'yepcollection.out');
  rewrite(output);
  readln(t);
  for i:=1 to t do work;
  close(output);
  close(input);
end.
題目來源:NDK 1368

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