1. 程式人生 > >POJ2533(Longest Ordered Subsequence 最長公共子序列 DP或單調佇列+二分)

POJ2533(Longest Ordered Subsequence 最長公共子序列 DP或單調佇列+二分)

Longest Ordered Subsequence
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 34454 Accepted: 15135

Description

A numeric sequence of ai is ordered if a1 < a2 < ... <aN. Let the subsequence of the given numeric sequence (a1,a2, ...,aN) be any sequence (ai1,ai2, ...,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

本題是一道經典的DP動態規劃問題,求最長上升子序列,序列不一定要連續,但是如果是求子串的情況就要求連續,用題目的樣例來說說思路,只要找出動態轉移方程此題就迎刃而解了,外層迴圈i遍歷整個陣列,內層迴圈j遍歷i之前的數,只要找到j小於i,即A[j] < A[i],便可以+1,寫出狀態轉移方程max(1,d[j]+1) + 1,所以置a[i]初始化為1,每次更新a[i]即可,最後比較每個a[i],最大的即為要求的結果。下面為程式碼:

/*
Title : Longest Ordered Subsequence
Author : minisheep
Running time:47MS
Memory cost:224K
*/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 1001;
int a[maxn],m;
int dp[maxn];
int LIS()
{
    int i,j,temp;
    temp = 1;
    for(i=1;i<=m;i++)
    {
        dp[i] = 1;
        for(j=1;j<i;j++)
        {
            if(a[j]<a[i] && dp[j] + 1 > dp[i])  //剛開始的時候寫成a[j]<= a[i] ,WA了幾次,沒看清題意,如果題意是求非下降子序列就要等於
            {
                dp[i] = dp[j] + 1;
                //cout<<dp[i]<<endl;
            }
        }
        if(dp[i] > temp)
        {
            temp = dp[i];
            //cout<<temp<<endl;
        }
    }
    return temp;
}
int main()
{
    int i,cnt;
    //freopen("1.in","r",stdin);
    while(cin>>m)
    {
        for(i=1;i<=m;i++)
        {
            cin>>a[i];
        }
        cnt = LIS();
        cout<<cnt<<endl;
    }
}
以上為2014 - 11月用DP寫的AC程式碼
---------------------------------------------------

時隔半年後(2015-07-11),用單調佇列+二分(時間複雜度nlogn)再度AC本題,一共跑了16ms.時間降了一倍多.

解題思路:

一個一個元素插入佇列,如果發現後者比當前的隊尾元素大那麼插入隊尾,否則二分查詢到比他大的最小值那個位置,替換掉即可。

程式碼如下:

#include<iostream>
#include<cstdio>
using namespace std;

const int maxn = 1005;

int Binary_Search(int *a,int left,int right,int element)
{
	int l = left;
	int r = right;
	int mid;
	while(l < r)
	{
		mid = (l + r) / 2;
		if(a[mid] <= element) l = mid + 1;
		else
			r = mid;
	}
	return l;
}

int main()
{
	int m;
	int i;
	while(cin>>m)
	{
		int t;
		int cnt;
		int a[maxn];
		cnt = 0;
		scanf("%d",&a[0]);
		cnt++; //一個元素插入佇列 
		//cout<<cnt<<endl;
		for(i=1;i<m;i++)
		{
			scanf("%d",&t);
			if(t > a[cnt-1])
			{
				a[cnt++] = t;
			}
			else
			{
				a[Binary_Search(a,0,cnt,t)] = t;
			}
		}
		printf("%d",cnt);
	}
	return 0;	
}