最长不下降子序列

**

最长不下降子序列

**
题目描述

一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < i;K <=N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8). 你的任务,就是对于给定的序列,求出最长上升子序列的长度。

输入

输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

输出

最长上升子序列的长度。

这道题呢,很显然是很普通的最长不下降子序列问题,只求长度即可。

所以思路也很简单,就是从头遍历,用一个一维数组 f [ i ] 来存储从第一个元素
到第 i 个元素的最长不下降子序列。复杂度 O(n^2);

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,ans,a[10005],f[10005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		f[i]=1; 
		//初始化;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(a[j]<a[i]){
				f[i]=max(f[i],f[j]+1);
			}
		}
		ans=max(ans,f[i]);
	}
	printf("%d",ans);
	return 0;
} 

这里可以看到按照这种写法在循环内部是一个一个元素遍历来找的,这里可以用 upper_bound ( ) 或者二分来优化,复杂度降到 O( n *log n );

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,ans=1,a[10005],b[10005]; 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
    	scanf("%d",&a[i]);
	}
    b[1]=a[1];
    for(int i=2;i<=n;i++){
        if(a[i]>=b[ans]){
        	b[++ans]=a[i];
		}
        else{
            int j=upper_bound(b+1,b+ans+1,a[i])-b;
            b[j]=a[i]; 
        }
    }
    printf("%d",ans);    
    return 0;
}

这是求最长不下降子序列的长度,如果要求输出求出的最长不下降子序列该怎么办呢。

要记录这个最长的上升序列,那么 n*logn 的 非 dp 做法似乎不行,这里用 O(n^2)的 dp 来写;

首先,用 f [ i ] 记录从 1 到 i 的最长上升序列长度,赋初值为 1 ;
用 b [ i ] 记录以 a [ i ] 为结尾的子序列的前一个元素位置;

当 a [ j ] < a [ i ] ,则当前的 a [ i ] 成为已知的以a [ j ] 结尾的最长序列的最新一位元素。

f [ i ] = max ( f [ i ] , f [ j ] + 1 );

如果更新了 f [ i ] 的值,则得到的序列是目前最长的序列;i 的前一位是 j ,表示为 b [ i ] = j ;记录下当前i的位置,表示为 times = i ;用 sum 记录序列最长长度 ;最后输出长度 sum ;

递归输出答案序列:

先找 cwy ( times ),表示从最后一位开始输出,一直向 b [ x ] 递归下去,直到 x = 0 ,回溯输出答案。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int f[100005],a[100005],b[100005];
//记录这个最长的上升序列,那么nlogn做法不行,用 n^2 dp ;
//f[i]表示到从a[1]到a[i]最长不下降子序列的长度;
//b[i]表示以a[i]结尾的子序列的前一个元素的位置; 
int sum,n,times;
void cwy(int x){
	//递归输出; 
	if(x==0){
		return;
	}
	//因为是从最后一个开始找的,所以要先递归,都找到后倒着再输出;	 
	else{
		cwy(b[x]); 
		printf("%d ",a[x]);
	}
}
int main()
{
	while(scanf("%d",&a[++n])!=EOF);
	n--;
	for(int i=1;i<=n;i++){
		f[i]=1;
	}
	//初值为 1 ; 
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(a[i]>a[j]){
				//a[i]可以成为已知以a[j]结尾的序列的最新一位元素; 
				if(f[i]<f[j]+1){
					f[i]=f[j]+1;
					b[i]=j;
					// i的前一位是j,表示为 b[i]=j; 
					if(sum<f[i]){
						//sum记录序列最长长度; 
						sum=f[i];
						times=i;
						//记录当前 i 的位置; 
					}
				}
			}
		}
	}
	printf("max=%d\n",sum);
	cwy(times);
	//递归输出;	 
	return 0;
}

这样就能输出最长不下降子序列了。
谢谢阅读。

本题样例输入:
300 250 275 252 200 138 245

样例输出:
max=2
250 275

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值