算法描述》關於LIS的nlogn方法


  上次TYVJ有一道裸LIS,然而我當時直接打了一個N^2暴力就草草了事,然后就ZZ了,只拿了60分,其實NlogN的LIS和N^2的差的不多,只是沒有N^2,好想罷了,鑒於某學弟的要求,所以就重現一下金哥當年講LIS的風范。

  首先,LIS指的是最長上升子序列。指的是我們要求出一個在母序列中找出一些元素,在保證這個子序列上升的同時,保證這個序列是整個母序列里滿足這一要求的最長序列。

  那么我們可以直接這樣想,我們要保證當前所拼接的鏈最大,那么對於每一個元素來說,可以直接一遍循環判斷它能夠屬於哪一個鏈,使當前以這個元素為結尾的鏈所接的元素個數最多,也就是所得的結果越大。

  那么對於一個元素ai,可以直接找a1到ai-1的元素中ai鏈在哪一個元素所得解最優,最優解放在f[i]里,這種對於所有情況的判斷,很明顯是DP,那么,轉移方程就是:f[i]=a[j]<a[i]?max(f[j]+1,f[i]):f[i]

  那么明顯的,這是一個N^2的算法,對於每一個元素要求以前是否有能夠找到更優解的上一步元素,一個判斷N遍。

  那如何得到一個更優算法捏?

  其實很簡單,讓我們先回顧剛才我們所做的操作,我們對於當前的一個元素,找前面所算過的所有結果,試圖找出更優解。我們在做這一工作時,也找了很多肯定不為最優解的元素,這是一件很浪費的事情,那么我們可以找一個優化這一過程的方法。

  我們知道,對於任意一個元素,使它的解更優的方案肯定是a[i]小,並且f[i]大的一個理想元素,因為對於這樣的一個理想元素,才可能使后面的解更優,而最大的f[i]是有限的,所以,我們很輕易得想到一個優化方法:

  使用一個數組d,用d[i]記錄當前  f[i]  為  i  的  a[i]最小的元素,d隨着元素的向后遞推逐漸維護。

  開一個數組就能使LIS更優嗎?答案是當然的,原因很簡單,我們發現,我們所維護的這個d數組是遞增的,這一結論可以通過反證法易證。既然這一數組是遞增的,我們就可以輕易的通過二分來得到最優解。

  所以,對於每一個元素做一遍二分,顯而易見復雜度是nlogn的。

 

附上一道水題 codevs 

 

LIS問題是最經典的動態規划基礎問題之一。如果要求一個滿足一定條件的最長上升子序列,你還能解決嗎?

    給出一個長度為N整數序列,請求出它的包含第K個元素的最長上升子序列。

    例如:對於長度為6的序列<2,7,3,4,8,5>,它的最長上升子序列為<2,3,4,5>,但如果限制一定要包含第2個元素,那么滿足此要求的最長上升子序列就只能是<2,7,8>了。

 

輸入描述  Input Description

第一行為兩個整數N,K,如上所述。

    接下來是N個整數,描述一個序列。

 

 

輸出描述  Output Description

請輸出兩個整數,即包含第K個元素的最長上升子序列長度。

 

樣例輸入  Sample Input

8 6

65 158 170 299 300 155 207 389

 

樣例輸出  Sample Output

4

 

數據范圍及提示  Data Size & Hint

80%的數據,滿足0<n<=1000,0<k<=n

    100%的數據,滿足0<n<=200000,0<k<=n

 

 1 #include<stdio.h>
 2 int cnt,n,a[200100],best[200100];
 3 void push(int x)
 4 {
 5     int L=0,R=cnt;
 6     int mid;
 7     while(L<=R)
 8     {
 9         mid=(L+R)/2;
10         if(x>best[mid]){
11             L=mid+1;
12             if(best[L]>x)best[L]=x;
13         }
14         else R=mid-1;
15     }
16 }
17 int main()
18 {
19     int k,i;
20     scanf("%d%d",&n,&k);k--;
21     for(i=0;i<=n-1;i++)
22         {
23         scanf("%d",&a[i]);
24         if(i>k&&a[i]<=a[k]){i--;n--;
25         }
26         }
27     for(i=0;i<=k-1;i++)
28     if(a[i]>=a[k])a[i]=0;
29     for(i=0;i<=n-1;i++)
30         {
31             if(a[i]<best[cnt]&&a[i])push(a[i]); 
32             else if(a[i])best[++cnt]=a[i];
33             }
34     printf("%d",cnt);
35     return 0;
36 }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM