最长上升子序列

最长上升子序列(密码6666)

最长上升子序列1

题目说明

给定一个长度为 N N N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N N N

第二行包含 N N N 个整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1 ≤ N ≤ 1000 1≤N≤1000 1N1000 − 1 0 9 ≤ a [ i ] ≤ 1 0 9 −10^9≤a[i]≤10^9 109a[i]109

输入样例

7
3 1 2 1 8 5 6

输出样例

4

题解

解题思路

假设 f [ i ] f[i] f[i] 表示以 a [ i ] a[i] a[i] 结尾的最长上升子序列,那么 f [ i ] = m a x { f [ 1 ] , f [ 2 ] , . . . f [ i − 1 ] } + 1 f[i]=max\left\{f[1],f[2],...f[i-1]\right\}+1 f[i]=max{f[1],f[2],...f[i1]}+1,并且取得的 m a x max max 代表的最长上升子序列合法。

AC_Code
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int n;
    cin>>n;
    vector<int>a(n+1);
    for(int i=1;i<=n;i++)
        cin>>a[i];
    vector<int>dp(n+1);
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
            if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
        ans=max(dp[i],ans);
    }
    cout<<ans;
    return 0;
}

最长上升子序列2

题目说明

给定一个长度为 N N N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N N N

第二行包含 N N N 个整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1 ≤ N ≤ 100000 1≤N≤100000 1N100000 − 1 0 9 ≤ a [ i ] ≤ 1 0 9 −10^9≤a[i]≤10^9 109a[i]109

输入样例

7
3 1 2 1 8 5 6

输出样例

4

题解

解题思路

这题和上一题的唯一区别就是数据范围的变化,从 1 ≤ n ≤ 1000 1\le n\le 1000 1n1000 变为 1 ≤ n ≤ 100000 1\le n\le 100000 1n100000 这就意味着我们需要更加高效的方法。

贪心原理

b b b 数组存的为枚举到第 i i i 个数时,长度为 j j j 的上升子序列结尾最小值为 b [ j ] b[j] b[j]

现在假设有两个结尾最小的上升子序列, C 1 1 , C 1 2 , . . . , C 1 i − 1 C1_1,C1_2,...,C1_{i-1} C11,C12,...,C1i1 C 2 1 , C 2 2 , . . . , C 2 i C2_1,C2_2,...,C2_i C21,C22,...,C2i 那么可以得到 C 1 i − 1 ≤ C 2 i − 1 C1_{i-1}\le C2_{i-1} C1i1C2i1,并且显然 C 2 i − 1 < C 2 i C2_{i-1}<C2{i} C2i1<C2i 那么可以推出 C 1 i − 1 < C 2 i C1_{i-1}<C2{i} C1i1<C2i 所以 b b b 数组一定是严格单调递增的。

AC_Code

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
    int n;
    cin>>n;
    vector<int>a(n+1);
    for(int i=0;i<n;i++)
        cin>>a[i];
  	vector<int>b(n+1,0x3f3f3f3f);
    int len=0;
    for(int i=0;i<n;i++)
    {
        int j=lower_bound(b.begin(),b.end(),a[i])-b.begin();
        b[j]=a[i];
        if(j+1>len)len++;
    }
    cout<<len;
    return 0;
}

FatMouse’s Speed

problem

FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to take the data on a collection of mice and put as large a subset of this data as possible into a sequence so that the weights are increasing, but the speeds are decreasing.

input

Input contains data for a bunch of mice, one mouse per line, terminated by end of file.

The data for a particular mouse will consist of a pair of integers: the first representing its size in grams and the second representing its speed in centimeters per second. Both integers are between 1 and 10000. The data in each test case will contain information for at most 1000 mice.

Two mice may have the same weight, the same speed, or even the same weight and speed.

output

Your program should output a sequence of lines of data; the first line should contain a number n; the remaining n lines should each contain a single positive integer (each one representing a mouse). If these n integers are m[1], m[2],…, m[n] then it must be the case that

W[m[1]] < W[m[2]] < … < W[m[n]]

and

S[m[1]] > S[m[2]] > … > S[m[n]]

In order for the answer to be correct, n should be as large as possible.
All inequalities are strict: weights must be strictly increasing, and speeds must be strictly decreasing. There may be many correct outputs for a given input, your program only needs to find one.

Sample

input
6008 1300
6000 2100
500 2000
1000 4000
1100 3000
6000 2000
8000 1400
6000 1200
2000 1900
output
4 4 5 9 7

题解

解题思路

直接用朴素版的dp,然后每次状态转移时记录一下使用的序列的最后一个元素的下标。这个题是没有顺序的,所以我们可以直接按重量从小到大排个序,这里需要注意要考虑重量相等的情况,需要将速度大的放到前面。

AC_Code
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int w[1005];
int s[1005];
int dp[1005];
int pre[1005];
struct A
{
	int w,s;
	int i;	
}a[1005];
int main()
{
	int n=0;
	int a0,b0;
	while(cin>>a0>>b0)
	{
		a[++n].w=a0;
		a[n].s=b0;
		a[n].i=n;
	}
	sort(a+1,a+1+n,[](A x,A y)
	{
		//if(x.w==y.w)return x.s>y.s;
		return x.w<y.w;	
	});
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(a[j].w<a[i].w&&a[j].s>a[i].s)
			{
				if(dp[i]<dp[j])
				{
					dp[i]=dp[j];
					pre[i]=j;
				}
			}
		}
		dp[i]++;
	}
	
	int maxi=0;
	for(int i=1;i<=n;i++)
	{
		if(dp[maxi]<dp[i])maxi=i;
	}
	cout<<dp[maxi]<<endl;
	vector<int>path;
	while(maxi!=0)
	{
		path.push_back(a[maxi].i);
		maxi=pre[maxi];
	}
	for(int i=path.size()-1;i>=0;i--)cout<<path[i]<<endl;
	return 0;
}

最少拦截系统

问题描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.

Input

输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)

Output

对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.

输入样例

8 389 207 155 300 299 170 158 65

输出样例

2

题解

解题思路

这里介绍一个dilworth定理😗**对于任意有限偏序集,其最大反链中元素的数目必等于最小链划分中链的数目.***对偶一下,我们要求最小LDS的划分数目,那么也就是求LIS的元素数目.

AC_Code

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
    int n;
    while(cin>>n)
    {
        vector<int>a(n+1);
        for(int i=0;i<n;i++)
            cin>>a[i];
        vector<int>b(n+1,0x3f3f3f3f);
        int len=0;
        for(int i=0;i<n;i++)
        {
            int j=lower_bound(b.begin(),b.end(),a[i])-b.begin();
            b[j]=a[i];
            if(j+1>len)len++;
        }
        cout<<len<<endl;
    }
    return 0;
}

最长公共子序列

题目描述

给出 1,2,…,n 的两个排列 P1 和 P2 ,求它们的最长公共子序列。

输入描述

第一行是一个数 n。

接下来两行,每行为 n 个数,为自然数 1,2,…,n 的一个排列。

输出描述

一个数,即最长公共子序列的长度。

输入样例

5 
3 2 1 4 5
1 2 3 4 5

输出描述

3

题解

解题思路

直接用最长公共子序列会TLE,所以我们要想办法优化。

题目描述中说明这个序列是一个排列,我们要尽可能的利用这个性质,两个长度相同的排列可以一一映射,并且对于最长公共子序列问题,我们将两个序列中的元素换成不与其它元素重复的元素显然是不冲突的,然后手玩一下就可以发现转化为了求最长上升子序列的问题,并且我们需要使用O(nlogn)的算法。

AC_Code
#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>
using namespace std;
unordered_map<int,int>mp;
int main()
{
	int n;
	cin>>n;
	vector<int>a(n+1);
	vector<int>b(n+1);
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	for(int i=1;i<=n;i++)mp[a[i]]=i;
	for(int i=1;i<=n;i++)b[i]=mp[b[i]];
	vector<int>t(n+1,0x3f3f3f3f);
	int len=0;
	for(int i=1;i<=n;i++)
	{
		int j=lower_bound(t.begin(),t.end(),b[i])-t.begin();
		if(j+1>len)len++;
		t[j]=b[i];
	}
	cout<<len;
	return 0;
}

怪盗基德的滑翔翼

题目描述

怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯。而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。

有一天,怪盗基德像往常一样偷走了一颗珍贵的钻石,不料却被柯南小朋友识破了伪装,而他的滑翔翼的动力装置也被柯南踢出的足球破坏了。不得已,怪盗基德只能操作受损的滑翔翼逃脱。

假设城市中一共有 N N N 幢建筑排成一条线,每幢建筑的高度各不相同。初始时,怪盗基德可以在任何一幢建筑的顶端。他可以选择一个方向逃跑,但是不能中途改变方向(因为中森警部会在后面追击)。因为滑翔翼动力装置受损,他只能往下滑行(即:只能从较高的建筑滑翔到较低的建筑)。他希望尽可能多地经过不同建筑的顶部,这样可以减缓下降时的冲击力,减少受伤的可能性。请问,他最多可以经过多少幢不同建筑的顶部(包含初始时的建筑)?

输入格式

输入数据第一行是一个整数 K ( K < 100 ) K(K<100) K(K<100),代表有 K K K 组测试数据。

每组测试数据包含两行:第一行是一个整数 N ( N < 100 ) N(N<100) N(N<100),代表有 N N N 幢建筑。第二行包含 N N N 个不同的整数,每一个对应一幢建筑的高度 h ( 0 < h < 10000 ) h(0<h<10000) h(0<h<10000),按照建筑的排列顺序给出。

输出格式

对于每一组测试数据,输出一行,包含一个整数,代表怪盗基德最多可以经过的建筑数量。

输入样例

3
8
300 207 155 299 298 170 158 65
8
65 158 170 298 299 155 207 300
10
2 1 3 4 5 6 7 8 9 10

输出样例

6
6 
9

题解

解题思路

由题意,由于怪盗基德飞行的方向不确定所以这题就相当于最长上升子序列和最长下降子序列取最大值。

AC_Code
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        vector<int>a(n+1,0);
        vector<int>dp1(n+1,0);
        vector<int>dp2(n+1,0);
        for(int i=1;i<=n;i++)cin>>a[i];
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
                if(a[i]>a[j])dp1[i]=max(dp1[i],dp1[j]);
                if(a[i]<a[j])dp2[i]=max(dp2[i],dp2[j]);
            }
            dp1[i]++;
            dp2[i]++;
            ans=max(ans,max(dp1[i],dp2[i]));
        }
		cout<<ans;
        if(t)cout<<endl;
    }
}

LIS of Sequence

problem

The next “Data Structures and Algorithms” lesson will be about Longest Increasing Subsequence (LIS for short) of a sequence. For better understanding, Nam decided to learn it a few days before the lesson.

Nam created a sequence a consisting of n (1 ≤ n ≤ 105) elements a1, a2, …, an (1 ≤ ai ≤ 105). A subsequence ai1, ai2, …, aik where 1 ≤ i1 < i2 < … < ik ≤ n is called increasing if ai1 < ai2 < ai3 < … < aik. An increasing subsequence is called longest if it has maximum length among all increasing subsequences.

Nam realizes that a sequence may have several longest increasing subsequences. Hence, he divides all indexes i (1 ≤ i ≤ n), into three groups:

  1. group of all i such that ai belongs to no longest increasing subsequences.
  2. group of all i such that ai belongs to at least one but not every longest increasing subsequence.
  3. group of all i such that ai belongs to every longest increasing subsequence.

Since the number of longest increasing subsequences of a may be very large, categorizing process is very difficult. Your task is to help him finish this job.

Input

The first line contains the single integer n (1 ≤ n ≤ 105) denoting the number of elements of sequence a.

The second line contains n space-separated integers a1, a2, …, an (1 ≤ ai ≤ 105).

Output

Print a string consisting of n characters. i-th character should be ‘1’, ‘2’ or ‘3’ depending on which group among listed above index i belongs to.

Examples

input
1
4
output
3
input
4
1 3 2 5
output
3223
input
4
1 5 2 3
output
3133

Note

In the second sample, sequence a consists of 4 elements: {a1, a2, a3, a4} = {1, 3, 2, 5}. Sequence a has exactly 2 longest increasing subsequences of length 3, they are {a1, a2, a4} = {1, 3, 5} and {a1, a3, a4} = {1, 2, 5}.

In the third sample, sequence a consists of 4 elements: {a1, a2, a3, a4} = {1, 5, 2, 3}. Sequence a have exactly 1 longest increasing subsequence of length 3, that is {a1, a3, a4} = {1, 2, 3}.

翻译

题目描述

下一节“数据结构和算法”将是关于序列的最长递增子序列(简称LIS)。为了更好地理解,他决定在上课前几天学习。

Nam 创建了一个由 n ( 1 ≤ n ≤ 1 0 5 ) n(1≤n≤10^5) n(1n105) 个元素 a 1 , a 2 , … a n ( 1 ≤ a i ≤ 1 0 5 ) a_1, a_2,…a_n(1≤a_i≤10^5) a1,a2an(1ai105) 组成的序列 a a a。其中一个子序列 a i 1 , a i 2 , . . . , a i k a_{i1},a_{i2},...,a_{ik} ai1,ai2,...,aik 被叫做上升子序列如果 a i 1 < a i 2   <   . . .   <   a i k a_{i1} < a_{i2}  < ... < a_{ik} ai1<ai2< ... <aik 并且 1 ≤ i 1 < i 2 < … < i k ≤ n 1≤i1 < i2 <…<ik≤n 1i1<i2<<ikn。如果一个上升子序列在所有上升子序列中长度最大,则称为最长上升子序列。

Nam 意识到一个序列可能有几个最长的递增子序列。因此,他将所有下标 i ( 1 ≤ i ≤ n ) i(1≤i≤n) i(1in) 分为三组:

1、 所有 a i a_i ai 的集合,使得 a i a_i ai 不属于最长递增子序列。
2 、 所有 a i a_i ai 的集合,使得 a i a_i ai 至少属于一个但不属于所有的最长递增子序列。
3、 所有 a i a_i ai 的集合,使得 a i a_i ai 属于每一个最长递增子序列。

由于 a a a 的最长递增子序列的数量可能非常大,因此分类过程非常困难。你的任务是帮助他完成这项工作。

输入描述

第一行包含单个整数 n ( 1 ≤ n ≤ 1 0 5 ) n(1≤n≤10^5) n(1n105),表示序列 a a a 的元素个数。第二行包含 n n n 个空格分隔的整数 a 1 , a 2 , . . . , a n ( 1 ≤ a i ≤ 1 0 5 ) a_1,a_2,..., a_n(1≤a_i≤10^5) a1,a2,...,an(1ai105)

输出描述

打印一个由 n n n 个字符组成的字符串。根据上面列出的索引 i i i 属于哪个组,字符应该是’1’,‘2’或’3’。

样例
输入样例1
1
4
输出样例1
3
输入样例2
4
1 3 2 5
输出样例2
3223
输入样例3
4
1 5 2 3
输出样例3
3133
提示

在第二个样本中,序列 a a a 4 4 4 个元素组成: { a 1 , a 2 , a 3 , a 4 } = { 1 , 3 , 2 , 5 } \left\{a_1,a_2, a_3, a_4\right\} =\left\{1,3,2,5\right\} {a1,a2,a3,a4}={1,3,2,5}。序列 a a a 恰好有 2 2 2 个长度为 3 3 3 的最长递增子序列,它们是 { a 1 , a 2 , a 4 } = { 1 , 3 , 5 } \left\{a_1, a_2, a_4\right\} =\left\{1,3,5\right\} {a1,a2,a4}={1,3,5} { a 1 , a 3 , a 4 } = { 1 , 2 , 5 } \left\{a_1, a_3, a_4\right\} =\left\{1,2,5\right\} {a1,a3,a4}={1,2,5}

在第三个样本中,序列_a_由4个元素组成: { a 1 , a 2 , a 3 , a 4 } = { 1 , 5 , 2 , 3 } \left\{a_1,a_2,a_3,a_4\right\} =\left\{1,5,2,3\right\} {a1,a2,a3,a4}={1,5,2,3}。序列 a a a 恰好有 1 1 1 个长度为3的最长递增子序列,即 { a 1 , a 3 , a 4 } = { 1 , 2 , 3 } \left\{a_1, a_3, a_4\right\} =\left\{1,2,3\right\} {a1,a3,a4}={1,2,3}

题解

解题思路

一个序列的最长上升子序列可能有多个,我们可以将这些序列用类似树的方式表示出来,第一层表示最长上升子序列第一个元素,然后第二层就是最长上升子序列第二个元素,例如,假如给你一个序列 1 3 2 4 5 ,那么用这种方法表示的最长上升子序列如下图所示

在这里插入图片描述

那么这样的话就很容易分类了,这个序列有两条最长上升子序列,显然 1、4、5 是两条最长上升子序列都要经过的,2、3 不是所有最长上升子序列都要经过的,所以 1、4、5 是第 3 类,2、3 是第二类,那么现在问题就是如何判断每一层的元素是否唯一。

从上面的最长上升子序列优化方案可以得到,每一个元素在更新 b b b 数组时得到的新数组表示的是,长度为 i i i 的上升子序列结尾最小的元素,这也代表了以这个元素结尾的上升子序列的最大长度,我们可以在用这个元素更新b数组时顺便将以这个与元素结尾的最长上升子序列长度给存下来,显然这个与元素结尾的最长上升子序列长度就是这个元素所在的层数(假如这个元素是最长上升子序列的元素),那么我们就可以判断这个元素是在第几层了,我们就可以通过判断在最长上升子序列的元素所在层数的数量来判断这一层的元素是第几类,但是我们怎么判断这个元素是否在这个序列的最长上升子序列里呢,枚举所有最长上升子序列是不现实的。

假如 x x x 是一个最长上升子序列里的元素,那么这个最长上升子序列可以被表示为以 x x x 结尾的最长上升子序列拼接上以 x x x 开头的最长上升子序列,这是因为以 x x x 结尾的最长上升子序列拼接上以 x x x 开头的最长上升子序列的长度是不可能比最长上升子序列的长度小的,并且显然也不可能比最长上升子序列的长度大,所以这种方法得到的就是最长上升子序列,所以我们假如我们求到了以一个元素开头的最长上升子序列长度加上以这个元素结尾的最长上升子序列的长度之和 − 1 -1 1 等于最长上升子序列的长度的话,那么这个元素就在最长上升子序列中。

那么如何求以这个元素开头的最长上升子序列的长度呢,很简单,就是和上面一样的思路,仅仅只需要将序列翻转并且全部取相反数就可以求到了。

AC_Code
#include<bits/stdc++.h>

#define int long long

using namespace std;
const int maxm = 2e6 + 5;
int lc[maxm], rc[maxm];
int ans[maxm];
int a[maxm];
int n;
void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++)cin >> a[i];
    vector<int>temp;
    //lc[i]表示以a[i]结尾的最大长度
    //求最长上升子序列
    for (int i = 1; i <= n; i++)
    {
        int p = lower_bound(temp.begin(), temp.end(), a[i]) - temp.begin();
        if (p == temp.size())temp.push_back(a[i]);
        else temp[p] = a[i];
        lc[i] = p + 1;
    }

    int ma = temp.size();
    //rc[i]表示以a[i]开头的上升子序列的最大长度
    //反转后求最长下降子序列
    //取反变为求最长上升子序列
    reverse(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i++)a[i] = -a[i];

    temp.clear();
    for (int i = 1; i <= n; i++)
    {
        int p = lower_bound(temp.begin(), temp.end(), a[i]) - temp.begin();
        if (p == temp.size())temp.push_back(a[i]);
        else temp[p] = a[i];
        rc[i] = p + 1;
    }

    reverse(rc + 1, rc + 1 + n);//记得翻转回去

    for (int i = 1; i <= n; i++)ans[i] = 1;
    map<int, int>mp;//mp储存以
    //ma就是最长上升子序列的长度
    for (int i = 1; i <= n; i++)
    {
        if (lc[i] + rc[i] - 1 == ma)//以a[i]为起点的最长上升子序列的长度加上以a[i]为终点的最长上升子序列的长度-1就是包含a[i]的最长上升子序列的长度
        {
            ans[i] = 2;
            mp[lc[i]]++;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        //长度为lc[i]的最长上升子序列只出现了1次,那么a[i]就是所有最长上升子序列的元素
        if (lc[i] + rc[i] - 1 == ma && mp[lc[i]] == 1)
        {
            ans[i] = 3;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        cout << ans[i];
    }
}
signed main()
{
    solve();
    return 0;
}

最长上升子序列

题目描述

给你一个长度为 n n n 的序列,请你求出去掉每个数的最长上升序列的长度是多少。

输入格式

第一行一个整数 n n n 表示有 n n n 个数。

接下来一行有 n n n 个正整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,表示这个序列中的每一个数。

输出格式

输出 n n n 个数,对于第 i i i 个数,表示去掉第 i i i 个数后最长上升序列的长度。

输入样例

5
1 3 2 4 5

输出样例

3 4 4 3 3

评测数据规模

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 6 1\le n\le 10^6 1n106

题解

解题思路

仔细想一想会发现这题和上面那题有一点关联,假如删除的元素是所有最长上升子序列的元素,那么就输出最长上升子序列的长度 − 1 -1 1,否则输出最长上升子序列的长度。所以这题的代码就是上一题的代码稍微在输出答案上改动了一下。这题还可以设置为给你 q q q 个询问,每次询问给一个 i i i,求删除第 i i i 个元素后的最长上升子序列。

AC_Code
#include<bits/stdc++.h>

#define int long long

using namespace std;
const int maxm = 2e6 + 5;
int lc[maxm], rc[maxm];
int a[maxm];
int n;
void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++)cin >> a[i];
    vector<int>temp;
    //lc[i]表示以a[i]结尾的最大长度
    //求最长上升子序列
    for (int i = 1; i <= n; i++)
    {
        int p = lower_bound(temp.begin(), temp.end(), a[i]) - temp.begin();
        if (p == temp.size())temp.push_back(a[i]);
        else temp[p] = a[i];
        lc[i] = p + 1;
    }

    int ma = temp.size();
    //rc[i]表示以a[i]开头的上升子序列的最大长度
    //反转后求最长下降子序列
    //取反变为求最长上升子序列
    reverse(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i++)a[i] = -a[i];

    temp.clear();
    for (int i = 1; i <= n; i++)
    {
        int p = lower_bound(temp.begin(), temp.end(), a[i]) - temp.begin();
        if (p == temp.size())temp.push_back(a[i]);
        else temp[p] = a[i];
        rc[i] = p + 1;
    }

    reverse(rc + 1, rc + 1 + n);//记得翻转回去
    
    map<int, int>mp;//mp储存以
    //ma就是最长上升子序列的长度
    for (int i = 1; i <= n; i++)
    {
        if (lc[i] + rc[i] - 1 == ma)//以a[i]为起点的最长上升子序列的长度加上以a[i]为终点的最长上升子序列的长度-1就是包含a[i]的最长上升子序列的长度
        {
            mp[lc[i]]++;
        }
    }
    for(int i=1;i<=n;i++)
    {
    	if(lc[i] + rc[i] - 1 == ma&&mp[lc[i]]==1)cout<<ma-1<<' ';
    	else cout<<ma<<' ';
	}
}
signed main()
{
    freopen("lis.in","r",stdin); //输入重定向,输入数据将从D盘根目录下的in.txt文件中读取 
    freopen("lis.out","w",stdout);
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值