Sicily 1060. Bridging Signals

這題挺簡單。

看圖就懂意思了,求不相交的線數目。

什麼時候兩條線交叉呢?   ---  i > j  但 a[i] < b[j] 的時候。

由於題目中a[i] = i ;

那就簡單地成爲了一個經典 ” 最長上升子序列 “問題啦。

數據到了w的級別,用 O(nlgn)的貪心方法做。


具體怎麼做呢?其實是用了額外的一個數組stk,其中stk[0]存下當前stk的長度,我們的目標是維護stk是遞增的

每次取待求數組的元素 signals[i], 將其與stk的最後一位相比較,如果比它大,則將它加入stk,當前stk的長度就是此時此刻的LIS

那麼如果比stk的最後一位小,那麼當然無法將其加入stk的末尾了。那麼此時把signal加入到stk中去替換掉“第一個出現比signal大的數”

依次這麼掃描待求數組並作處理,最後stk的長度就是LIS的長度了。


至於爲什麼這麼做,請允許我爲《算法引論》做個小解讀吧(純粹個人意見,可能錯得離譜)。


回顧我們想要的目標——最長上升子序列,下稱LIS ( Longest Increasing Sequence)


歸納假設:假設我們知道如何對前m個數求其長度爲k(k從1開始遞增到最長)的最有潛力的LIS,

最有潛力——什麼意思呢?也就是更方便我們後面讀到數之後加入其中。

那麼它必須是長度爲k的LIS裏(可能有多個),結尾數最小的。


那麼當處理第m+1個數的時候,我們有很多種處理情況——

1.首先如果它比最長的那個LIS(比如它的長度是x?)的尾巴還大,那麼可以直接加入末尾,得到一個新的長度爲x+1的一個LIS(原來那個長度x的LIS還是以原來的尾巴結尾,跟這個不矛盾)。

2.如果不是,那麼我們逐次去看長度爲(x-1, x-2, 。。。1)的LIS,看能否加入其中的某個,組成一個新的。

來了——如果可以加入,那麼組成一個新的,勢必會跟原來的某個LIS長度相等了,這時他們就要“競爭”了,PK掉一個,PK掉那個尾巴大的,留下尾巴小的,那麼可以給未來更大的可能。


於是在這種情況下,我們可以知道,每次我們總能找到一個LIS,然後去遞增它的長度,right?如果實在不行,那麼它自己成爲一個長度爲1的LIS,並理所當然地PK掉其他長度爲1的LIS(反正:如果不能PK掉它,那說明它比那個大,那麼可以接在後面組成長度2的LIS,對吧)。


當我們把所有元素掃描完,我們自然就得到了最長的LIS了,而且保證不會錯過任何一個可能(就是那些本來不是最長,後來慢慢增長並超越冠軍成爲最長的LIS)。


觀察到我們上面只提到這些LIS的“尾巴”,於是我們只需要存下“尾巴”們和它們代表的LIS的長度就夠了。

那麼用一個數組完全可以做到這一點,stk[i] 表示 長度爲 i 的LIS現在的尾巴。

現在我們明白stk中“替換”的操作,其實就是“PK掉尾巴大的數”的過程了。


-這裏可能有個小疑問——爲什麼長度 k+1 的LIS的尾巴,一定不會比 長度 k 的LIS小呢?(爲什麼stk會是遞增的)

因爲如果不是這樣,那麼 長度 k 的那個,完全可以拿這個k+1的前k個去當LIS呀。


現在,我們得到了一個完整的算法了。由於stk有序,每次可以直接找到它該去的位置,二分找。


代碼:


#include <iostream>
#include <stdio.h>
using namespace std ;

int main () {
    int t,p ;
    // s 是容器 , a是收到的信號(圖) 
    int stk[40005] , signals[40005] ;
    int low , high , mid ;
    scanf ( "%d" , &t ) ;
    while ( t-- ) {
        scanf ( "%d" , &p ) ;
        for ( int i = 0 ; i < p ; ++i ) scanf ( "%d" , &signals[i] ) ;
        stk[0] = 1 ;
        stk[1] = signals[0] ;
        for ( int i = 1 ; i < p ; ++i ) {
            // 二分查找容器中第一個比a[i]大的數 
            low = 1 ;
            high = stk[0] ;
            while ( low <= high ) {
                mid = (low-high)/2+high ;
                if ( stk[mid] < signals[i] ) {
                    low = mid+1 ;
                }
                else {
                    high = mid-1 ;
                }       
            }
            if ( low == stk[0]+1 ) ++stk[0] ;  
            stk[low] = signals[i] ;
        }
        printf ( "%d\n" , stk[0] ) ;
    }
    //system("pause") ;
    return 0 ;
} 


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