Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 33367 | Accepted: 14592 |
Description
Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.
Input
Output
Sample Input
7 1 7 3 5 9 4 8
Sample Output
4
這道題是一道很簡單的LIS,經典的DP模型。由於今天學習的是DP優化,就用這題來寫,把LIS的O(n^2)算法優化成O(nlogn)。
用什麼方法呢,方法有兩種。
方法一:
先寫出狀態轉移方程,F[i]代表以A[i]結尾的最長上升子序列長度。 F[i] = max( F[j]: i>j,A[i]>A[j])+1;
注意到,F[j]是所有A[i]小於A[j]中的最大值,我們不妨按A[i]的順序維護一棵線段樹,這樣就可以把O(n)查詢最大值的複雜度降爲O(logn)。故總複雜度就降爲O(nlogn);
方法二:
用dp[i]代表長度爲i的最長上升子序列的最小末尾數,則對於每次加入新元素可以進行維護。如果a[i]<b[1],則k=1
若a[i]>b[len],則k = ++len;否則就在b[i]中二分 找到比b[x]<a[i]<b[x+1],(因爲b[i]是遞增的,容易證明)並替換,模版如下
int LIS(int n){
dp[1] = num[1];
int len = 1,j;
for(int i=2;i<=n;i++){
if(a[i]<=dp[1]) j = 1; //if(a[i]<dp[1]) j = 1;
else if(a[i]>dp[ans])j = ++ans; //else if(a[i]>=dp[ans]) j = ++ans;
else j = lower_bounder(dp,dp+ans,a[i])-dp; //else j = upper_bounder(dp,dp+ans,a[i])-dp;
dp[j] = a[i];
}
return len;
}
兩種方法的AC代碼:
線段樹:
#include <iostream>
#include <cstdio>
#include <cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 1005;
const int N = 10000+1;
int maxx[N<<2],num[maxn],n,F[maxn];
void pushup(int rt){
maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l == r){maxx[rt] = 0;return;}
int m = (l+r)>>1;
build(lson);
build(rson);
}
void add(int pos,int val,int l,int r,int rt){
if(l == r){
maxx[rt] = val;
return;
}
int m = (l+r)>>1;
if(pos<=m)add(pos,val,lson);
else add(pos,val,rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L <= l&& R >= r){
return maxx[rt];
}
int m = (l+r)>>1,ret = 0;
if(L<=m)ret = max(ret,query(L,R,lson));
if(R>m)ret = max(ret,query(L,R,rson));
return ret;
}
int main()
{
while(scanf("%d",&n)==1){
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
memset(F,0,sizeof(F));
memset(maxx,0,sizeof(maxx));
build(1,N,1);
F[1] = 1;
add(num[1]+1,F[1],1,N,1);
int ans = 1;
for(int i=2;i<=n;i++){
F[i] = query(1,num[i],1,N,1)+1;
ans = max(F[i],ans);
add(num[i]+1,F[i],1,N,1);
}
cout<<ans<<endl;
}
return 0;
}
方法二:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1005;
int num[maxn],b[maxn],n;
int main()
{
while(scanf("%d",&n)==1){
for(int i=1;i<=n;i++)scanf("%d",&num[i]);
b[1] = num[1];
int len = 1,k;
for(int i=2;i<=n;i++){
if(num[i]>b[len])k = ++len;
else if(num[i]<b[1]) k = 1;
else k = lower_bound(b,b+len,num[i])-b;
b[k] = num[i];
}
cout<<len<<endl;
}
return 0;
}