題目連接:http://poj.org/problem?id=2533c++
Time Limit: 2000MS Memory Limit: 65536Kspa
Descriptioncode
Inputblog
Outputip
Sample Inputci
7 1 7 3 5 9 4 8
Sample Outputelement
4
題意:get
給定長度爲 $N$ 的一串整數序列 $a[1 \sim N]$,求其最長上升子序列的長度。input
注意:子序列能夠不連續,要求嚴格單增。it
題解:
$O(n \log n)$ 解法——貪心+二分。
構建一個棧 $S$ 和一個變量 $top$ 表明棧頂位置,該棧的表明:棧中的第 $i$ 個數 $S[i]$,是序列 $a$ 中,長度爲 $i$ 的遞增子序列的末尾元素。
初始化 S[top=1]=a[1] ,即將第一個數字入棧;這很好理解,到目前爲止 $a[1]$ 本身是一個長度爲 $1$ 的遞增子序列。
遍歷 $a[ i = 2 \sim N ]$:每次對於 $a[i]$,找出棧 $S[1 \sim top]$ 中第一個大於等於 $a[i]$ 的數的位置 $pos$,若不存在則返回 $pos=top+1$。
這是因爲,若存在第一個大於等於 $a[i]$ 的數 $S[pos]$ ,說明對於長度爲 $pos$ 的遞增子序列,能夠用 $a[i]$ 代替掉其原來的末尾元素 $S[pos]$,這樣一來,依然是一個長度爲 $pos$ 的遞增子序列,並且該遞增子序列被進一步「加長」的潛力增長。而若是棧中不存在大於等於 $a[i]$ 的數,這說明我能夠在目前長度爲 $top$ 的遞增子序列後面加上一個 $a[i]$,那麼咱們就獲得了一個以 $a[i]$ 爲結尾的,長度爲 $top+1$ 的遞增子序列。
所以,咱們把 $S[pos]$ 更新爲 $a[i]$,而且嘗試更新棧的大小 if(pos>top) top=pos; 。
因爲棧 $S$ 中元素始終保持單調遞增(並且棧內元素互不相等),因此找 $S$ 中第一個大於等於 $a[i]$ 的數能夠使用二分查找。
AC代碼(在OpenJudge百練提交):
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+5; int n; vector<int> a; int S[maxn],top; int LIS(const vector<int>& a) { S[top=0]=a[0]; for(int i=0;i<a.size();i++) { int pos=lower_bound(S,S+top+1,a[i])-S; S[pos]=a[i], top=max(top,pos); } return top+1; } int main() { cin>>n; while(n--) { int x; cin>>x; a.push_back(x); } cout<<LIS(a)<<endl; }
PS.咱們能夠看到,求第一個大於等於 $a[i]$ 的數使用了lower_bound,相應的若是咱們使用upper_bound會怎麼樣呢?不難證實,咱們將會獲得最長不降低子序列的長度。