最長不下降子序列問題
最長上升子序列問題是解決很多問題的根本,它能幫助你理解二分的思想。
引言
考慮一下:對於一個序列 ,請你查找中最長的子序列,使得任意 時 .
例如一個長度爲的= ;
顯然,它的最長不下降子序列就是 .
我們可以想一下自己是如何看出它的最長不下降子序列的.
首先,第一個數是,前面沒有數,所以它可以是子序列的一部分,那麼我們可以將它放到考慮的第一位:
? ? ? ?
因爲放了一個數,所以答案要加一:
第二個數是,有兩種選擇,一種是插入到剛插入的數的後面,第二種就是替換掉.因爲,所以不可以放到的後面去.比大還在答案中,那我要你有什麼用?果斷替換:
? ? ? ?
第三個數是,與同理,替換:
? ? ? ?
第四個數是,比剛插入的數小,可以插入,那麼就變爲:
? ? ?
第五個數同理插入:
? ?
至此,我們的大腦處理完了這樣一個長度爲的最長不下降子序列,當長度很小時我們能順利解決,剩下的就交給計算機啦.
二分求解
在我們模擬的時候,有這樣一個操作,當新讀入的數字小於答案數列的第個數的時候,我們需要找到要用讀入數字替換掉的位置.這個時候,選擇從第個數挨個比對到第個數就很睿智 ,於是我們選擇二分求解.
看這樣一個序列:
假設新讀入的數是3.
那麼我們取這個元素個數爲的序列的中間數:
比大,而這個序列是單調遞增的,要替換的位置一定在左側.我們取左側的序列.
中間數爲, ,那麼我們取右側的序列
可以判斷,序列中只有一個元素,要替換的數就決定是它啦!
這樣子尋找就省去了的複雜度轉爲n了,爲我們節省了很多時間.
代碼實現
#include <cstdio>
#include <iostream>
#define maxn 100001
#define minn -99999
using namespace std;
int n,a[maxn],f[maxn],ans;//f數組就相當於上面的答案數組,a數組存的是數的值.
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
f[0]=minn;
for(int i=1;i<=n;i++){
if(a[i]>f[ans]){//直接向答案序列後加數
f[ans+1]=a[i];
ans++;
}
else{
int l=0,r=ans;
while(l<r){
int mid=(l+r)>>1;
if(f[mid]>a[i]){
r=mid;//如果中間數比要加的數大,那麼就要取左側序列
}
else{
l=mid+1;//如果中間數比要加的數小,那麼就要取右側序列
}
}
f[l]=a[i];//最後替換
}
}
printf("%d\n",ans);
return 0;
}
更簡潔的寫法
你可能會問了:有沒有更簡潔的寫法來實現這樣高效的二分呢?答案是有的.在我們的c++ STL庫中就有這樣的函數,來幫助我們實現查找.
在< algorithm > 庫中,有個叫lower_bound的函數,先看他的函數定義:
template< class ForwardIt, class T >
ForwardIt lower_bound( ForwardIt first, ForwardIt last, const T& value );
它的功能是:
返回指向範圍 [first, last) 中首個不小於(即大於或等於) value 的元素的迭代器,或若找不到這種元素則返回 last 。
如果想了解更多關於lower_bound的嚴格說明,請點擊傳送門
那麼我們有了這個工具之後就可以這樣改進我們的程序,讓它更簡潔.
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define maxn 100001
using namespace std;
int n,a[maxn],f[maxn],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
memset(f,10000,sizeof f);//要初始化的大,後面的比較就不會因爲值爲0出錯
f[0]=-1;//vis[0]除外,它要設爲-1
for(int i=1;i<=n;i++){
int v=lower_bound(f,f+1+ans,a[i])-f;//在1到ans的區間中尋找第一個比a[i]大的.
ans=max(ans,v);//更新答案
f[v]=min(f[v],a[i]);//替換掉比a[i]大的
}
printf("%d\n",ans);
return 0;
}
這樣就將二分的過程運用STL函數省去了,提高了我們的編程效率.