POJ 2553 最长上升子序列

http://poj.org/problem?id=2533

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]表示以a[i]结尾的上升子序列的长度。外层循环i从1到n遍历原序列,内层循环j从1到i,当a[j]<a[i]时,有转移方程:dp[i]=max(dp[i],dp[j]+1)。最后要遍历dp数组找最大值。最长上升子序列可不一定以最后一个元素结尾。

#include<iostream>
#include<cstdio>
#include<stack>
#include<cmath>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<iterator>
#define INF 0x3f3f3f3f
#define EPS 1e-10
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

int dp[1005];
int a[1005];

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		dp[i]=1;
	}
	for(int i=0;i<n;i++)
		for(int j=0;j<i;j++)
			if(a[j]<a[i])
				dp[i]=max(dp[i],dp[j]+1);//以a[i]结尾的最长子序列的长度
	int MAX=0;
	for(int i=0;i<n;i++)
		MAX=max(MAX,dp[i]);
	printf("%d\n",MAX);
	return 0;
}

方法二:

思路:因为求的是最长上升子序列,因此我们考虑维护一个单调增的数组low,初始化low[0]=a[0],然后从1到n-1遍历原序列,若a[i]大于当前low数组的最后一个值,那么把a[i]直接接到后面;反之,我们在low数组中二分查找到第一个大于等于a[i]的元素,(因为这里是上升子序列 而不是非减子序列)并用a[i]替换掉这个元素。这里用到的是贪心的思想,因为你想找一个最长的上升子序列,那么靠前的元素越小越好,而且将后面的元素插入、替换到low数组中,并没有什么问题,因为长度并没有发生变化,这样做只是为了搜寻可能的最优解。

#include<iostream>
#include<cstdio>
#include<stack>
#include<cmath>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<iterator>
#define INF 0x3f3f3f3f
#define EPS 1e-10
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

int low[1005];
int a[1005];

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",&a[i]);
	int k=0;
	low[0]=a[0];
	for(int i=1;i<n;i++)
	{
		if(a[i]>low[k])//a[i]大于当前low数组中最大的 可以直接放到后面
			low[++k]=a[i];
		else	//否则在low数组找到第一个大于或等于a[i]的替换 因为这里是严格上升的 否则可用upper_bound
			low[lower_bound(low,low+k+1,a[i])-low]=a[i];
	}
	printf("%d\n",k+1);
	return 0;
}

方法三:

思路:树状数组优化。朴素的dp方程是:dp[i]=max(dp[j]+1,dp[i]),(1<=j<i且a[j]<a[i])。因为每次内层都要扫一遍寻找dp[j]的最大值,所以时间复杂度较高。我们读入序列a,同时记录第i个元素的下标,对序列a按照权值从小到大排序(这样就满足了a[j]<a[i]了),那么正向迭代的时候,只需要考虑下标的问题,即对于a[i],我们只需要知道以a[j]结尾且j<i的最长上升子序列的长度,用树状数组来维护:tree[i]=max(dp[a[i]],dp[a[i-1]],dp[a[i-2]],………dp[a[i-lowbit(i)+1]])+1即可。反着来也可以,即对序列a的权重做离散化处理(避免越界),此时正向迭代满足了i<j,只需要考虑权值问题。

最长上升:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;


struct node
{
    int v,id;
    bool operator <(const node &a)const
    {
        return v<a.v;
    }
};
int tree[1005];
node a[1005];
node b[1005];
int n;

inline int lowbit(int x)
{
    return x&(-x);
}

void update(int i,int v)
{
    for(;i<=n;i+=lowbit(i))
        tree[i]=max(tree[i],v);
}

int query(int i)
{
    int ans=0;
    for(;i;i-=lowbit(i))
        ans=max(ans,tree[i]);
    return ans;
}

int main()
{
    while(~scanf("%d",&n))
    {
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].v);
            a[i].id=i;
        }
        sort(a+1,a+1+n);
        b[1].id=a[1].id;
        b[1].v=a[1].v;
        int len=1;
        for(int i=2;i<=n;i++)
        {
            if(a[i].v==b[len].v)
                continue;
            b[++len].v=a[i].v;
            b[len].id=a[i].id;
        }
        int temp;
        int MAX=0;
        for(int i=1;i<=len;i++)
        {
            temp=query(b[i].id);
            update(b[i].id,temp+1);
            MAX=max(MAX,temp+1);
        }
        printf("%d\n",MAX);
    }
    return 0;
}

最长非降: 

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;


struct node
{
    int v,id;
    bool operator <(const node &a)const
    {
        return v<a.v;
    }
};
int tree[1005];
node a[1005];
int n;

inline int lowbit(int x)
{
    return x&(-x);
}

void update(int i,int v)
{
    for(;i<=n;i+=lowbit(i))
        tree[i]=max(tree[i],v);
}

int query(int i)
{
    int ans=0;
    for(;i;i-=lowbit(i))
        ans=max(ans,tree[i]);
    return ans;
}

int main()
{
    while(~scanf("%d",&n))
    {
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i].id=i;
        }
        sort(a+1,a+1+n);
        int temp;
        int MAX=0;
        for(int i=1;i<=n;i++)
        {
            temp=query(a[i].id);
            update(a[i].id,temp+1);
            MAX=max(MAX,temp+1);
        }
        printf("%d\n",MAX);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值