最大上升子序列,最大下降子序列,最大非增子序列

For example,{1,5,2,4,3,5,6,4,7}的最大上升子序列是{1,2,3,5,6,7}长度为6

现已知原序列a[],如何求其最大上升子序列,最大下降子序列,最大非增子序列,最大非减子序列的长度?
下面贴出两种方法:1.dp, 2.贪心+二分

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

#define maxn 1000
const int inf=1<<30;
int a[maxn];//原序列 

int dp[maxn];//dp[i]代表以a[i]结尾的最大非增子序列
int lnip1(int n) //动态规划,时间复杂度O(n2)
{
	for(int i=0;i<n;i++)
	dp[i]=1;
	for(int i=1;i<n;i++)
	{
		for(int j=0;j<i;j++)
		{
			if(a[j]>=a[i])//最大非增子序列,若改为a[j]>a[i],即为最大递减子序列
			{
				dp[i]=max(dp[j]+1,dp[i]);
			}
		}
	}
	int ans=0;
	for(int i=0;i<n;i++)
	{
		ans=max(ans,dp[i]);
	}
	return ans;

 } 

 int b[maxn];//b[k] 记录前i个数中的长度为k的所有子序列中最后一位最小的值,b数组一定有序
 int lndp(int n)//最大非递减子序列长度,时间复杂度O(nlogn)
 {
 	for(int i=0;i<=n+1;i++)
 	b[i]=inf;
 	for(int i=0;i<n;i++)
 	{
 	    int pos=lower_bound(b,b+i+1,a[i])-b;//如果是严格递增(lip)用upper_bound
 		b[pos]=a[i];
	 }
	 int ans;
	 for(ans=0;b[ans]!=inf;ans++);
	 return ans;
	//如果题目要求求最大非递增子序列(lnip)长度,只需先把数组反过来,再求lndp即可,最大递减类似
 }

 int main()
 {
 	int n;
 	cin>>n;
 	for(int i=0;i<n;i++)
 	cin>>a[i];
 	cout<<lnip1(n)<<endl;
 	cout<<lndp(n);
 }

第一种方法比较简单,不做过多详解_

下面详解第二种贪心+二分,时间复杂度位O(nlongn)的算法(注意:下面以严格上升子序列为例,请把上面代码中的lower_bound换成upper_bound):

首先,

b[maxn] b[k] 记录前i个数中的长度为k+1的所有上升子序列中最后一位最小的值
记录下最后一位最小的值,是为了能有更大的上升空间
比如序列{1,3,2,3,4}
前三个数{1,3,2}中长度为2的上升子序列有{1,3}和{1,2},分别对应的b[1](长度为1+1=2的上升子序列的最后一位的值)为3,2,明显,选2这个较小的可以给后面的序列留下更多的上升空间,选2可以是{1,2,3,4}而选3只能是{1,3,4},b[k]的值尽可能小,
这就是其贪心的思想
b数组一定是严格上升的,因为长度为i的最后一位最优不可能比长度为i-1的最后一位小。
既然b数组是有序的,接下来就能用二分的方法了
先将b的元素最大化,然后将将原序列a的元素一个一个插入到b中合适的位置--b中第一个大于a[i]的位置,这样如果a[i]能够大于b[k]说明a[i]能够放在b[k](其实是b[k]的值对应的原序列元素)的后面,而到了第一个大于a[i]的位置,
假设那个位置x上的的值为y(y可以为inf),a[x]=y>a[i],再根据上面贪心的思想,何不把结尾换为更小的值a[i]呢,即b[x]=a[i];
这样插入完所有的a[i](其实就是将a[i]插在能以它为结尾的最大子序列长度的位置上),一重循环到最后一个b[i]!=inf,然后这个i+1就是最大上升子序列的长度

接下来再以{1,5,2,4,3,5,6,4,7}为例,画个图让大家理解理解:

(字有点差,勿喷)

时间: 2024-10-07 19:04:29

最大上升子序列,最大下降子序列,最大非增子序列的相关文章

【一维偏序】【最长上升子序列】【最长非降子序列】

两种方法,都是nlogn 树状数组型 #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int N=100003,M=50003; int d[N];//原数组 int f[M]; int n,big; int lowbit(int x) { return x&(-x); } //用于求上升序列 int

[pythontip]最大非连续子序列

题目链接:http://www.pythontip.com/coding/code_oj_case/36给你一个整数list L, 如 L=[2,-3,3,50], 求L的一个非连续子序列,使其和最大,输出最大子序列的和. 这里非连续子序列的定义是,子序列中任意相邻的两个数在原序列里都不相邻. 例如,对于L=[2,-3,3,50], 输出52(分析:很明显,该列表最大非连续子序列为[2,50]). dp首先复制L序列的元素,然后比较前两个元素,确定最优解赋值给dp[1]. 1 # L=[2,-3

最长非上升子序列的长度

最长非上升子序列问题是一个经典的DP问题.如下给出完整的问题描述: 给你一串序列 A1,A2,A3,A4,A5........An.让你找出它的某个最长子序列 S1,S2,S3,S4.........Sm.使得 S1<=S2<=S3<=S4.........<=Sm. 从问题描述中,我们可以把 <= 换成各种其他的关系符号从而变成最长非下降子序列等,他们都只是关系描述不同,其本质都是一样的.现在,我们来只需总结一下最长非上升子序列问题即可应用到其他类型. 这既然是一个经典的D

动态规划-最长非降子序列

有关概念: 最长上升子序列(LIS,Longest Increasing Subsequence),在一个序列中最长的单调递增的子序列 思路: 求LIS通常有O(n2)和O(nlogn)两种算法 (1)O(n2)算法 fi表示以第i个数结尾的LIS长度 对于序列中的一个数i,在i前面枚举数j,j满足比i小且fj最大,将i作为j的后继 伪代码&状态转移方程: f1=1 for i=2...n for j=1...i-1 if(aj<ai)fi=max(fi,fj+1) 最后结果在f1~fn中取

[LeetCode] Longest Uncommon Subsequence I 最长非共同子序列之一

Given a group of two strings, you need to find the longest uncommon subsequence of this group of two strings. The longest uncommon subsequence is defined as the longest subsequence of one of these strings and this subsequence should not be any subseq

Codeforces Round #323 (Div. 2) D. Once Again... 暴力+最长非递减子序列

                                                                              D. Once Again... You are given an array of positive integers a1, a2, ..., an × T of length n × T. We know that for any i > n it is true that ai = ai - n. Find the length of

最长上升非降子序列的长度动态规划

第一种dp从后往前: dp[i]表示以a[i]为起点的最长上升非降子序列的长度 a[8]={10,2,2,4,12,23,34,2} dp[8]={4,6,5,4,3,2,1,1}; 代码实现: 1 #include<bits/stdc++.h> 2 using namespace std; 3 void logest_increase_sub(const int*a,int ssize) 4 { 5 int *dp=new int[ssize](); 6 int *p=new int[ssi

poj 1631 最多能有多少条不交叉的线 最大非降子序列 (LIS)

左边的数字是1 2 3 4 5.... 右边的数字 第一个输入的和1连 第2个输入的和2连 右边再按从小到大排序 要求连线不能交叉 问最多能有多少条不交叉的线 假如右边有5个1 那么答案会是5 所以是最大非降子序列 Sample Input4 //T6 //n426315 Sample Output 3 1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <al

HDU 1025 Constructing Roads In JGShining&#39;s Kingdom[动态规划/nlogn求最长非递减子序列]

Constructing Roads In JGShining's Kingdom Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 27358    Accepted Submission(s): 7782 Problem Description JGShining's kingdom consists of 2n(n is no mor