POJ 2533 Longest Ordered Subsequence (最長上升子序列 DP優化)

Longest Ordered Subsequence
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 33367   Accepted: 14592

Description

A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1a2, ..., aN) be any sequence (ai1ai2, ..., aiK), where 1 <= i1 < i2 < ... < iK <= N. For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8).

Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.

Input

The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, separated by spaces. 1 <= N <= 1000

Output

Output file must contain a single integer - the length of the longest ordered subsequence of the given sequence.

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;
}


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