也谈最长不降子序列的长度

题目是一本通网站(ybt.ssoier.cn:8088)中1259:【例9.3】求最长不下降序列

【题目描述】

设有由n(1n200)n(1≤n≤200)个不相同的整数组成的数列,

记为:b(1)b(2)b(n)b(1)、b(2)、……、b(n)且b(i)b(j)(ij)b(i)≠b(j)(i≠j),

若存在i1<i2<i3<<iei1<i2<i3<…<ie且有b(i1)<b(i2)<<b(ie)b(i1)<b(i2)<…<b(ie)

则称为长度为e的不下降序列。程序要求,当原数列出之后,求出最长的不下降序列。

例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。

例中13,16,18,19,21,22,63就是一个长度为7的不下降序列,

同时也有7 ,9,16,18,19,21,22,63组成的长度为8的不下降序列。 

【输入】

第一行为n,第二行为用空格隔开的n个整数。

【输出】

第一行为输出最大个数max(形式见样例);

第二行为max个整数形成的不下降序列,答案可能不唯一,输出一种就可以了,本题进行特殊评测。

【输入样例】

14

13 7 9 16 38 24 37 18 44 19 21 22 63 15

【输出样例】

max=8
7 9 16 18 19 21 22 63

其实这个题很多的网文都说到了,我也看了好久才看懂,然后把我的理解发
出来大家下,希望还没看懂别的博文的朋友这次能让你懂。
在众多的网文中,
https://blog.csdn.net/sdz20172133/article/details/79265834
这位兄弟pxlsdz说的算比较详细的了,感谢你。

输入的序号

1

2

3

4

5

6

7

8

9

10

11

12

13

14

输入的值

13

7

9

16

38

24

37

18

44

19

21

22

63

15

不降序列的长度

1

1

2

3

4

4

5

4

6

5

6

7

8

3






我们可以用数组来记录不降序列的长度
一个一个数字的解释吧。第一个数13,
当然长度是1,就一个数嘛。第2个数7的长度也是1,共就两个数,7比13小,不
能形成长度为2的不降序列,就是1了。实际上这个时候可以理解为有两个序列了,
一个是13开头,一个是7开头。第3个数9,它可以接在7后面,不能接在13后面,
当然没必须再开一个新序列(不是最长嘛),所以是2。第4个数16可以接在13的
后面,那13的长度可以为2,但如果16接在9的后面,长度那形成的序列是7,9,
16,长度为3,显然应接在9后面,第5个数38接在16的后面,长度为4。这个时候
有一个长度为4的序列7,9,16,38。第6个数24,它不能接在38后面,但可以
接在16的后面,也就是如果要把24加入一个最长序列的话,那只是不要38了,
这将形成一个序列7,9,16,24,长度为4。第7个数37当然可以接在24的后面,
形成一个度为5的序列。(如果第7个数不是37,而是38,那39可以接在38后面,
也可以接在24的后面,那长度都一样)。第8个数18来了,它只能接在16的后面,
形成一个长度为4的序列。第9个数可以接在38、24、37、18的任何一个数的后
面,那接在谁的后面能获得最大长度呢?当然是37了,可以得长7,9,16,24,
37,44的序列,长度相比而言应该是最长的了。所以44的长度是6。接着19,21,
22当然顺次接到18的后面就好,所以长度分别为5,6,7。63应该可以接到44的
后面,长度为7,也可以接到22的后面,长度为8,那当然得往大的方面走了,所
以63的长度定为8。最后一个15只能接到9的后面,长度自然是3。每一个数的序
列长度实际上是把它与前面的数比较,找到比它小对应长度又最大的数就好(从
后面往前面找,长度也就是找数的升序+1)打擂记录最大长度就得到答案了。如
果要找出最长子序列,那就需要记录每一个数的前一个数了,当然记序号也可。
//求最长不降子序列长度,一本通网站1259题 
#include<iostream>
using namespace std;
int a[201],b[201],c[201],mx=1,mxn;
void pr(int x)
{
    if(b[x]>1)pr(c[x]);
    cout<<a[x]<<' ';
    return;
}
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=1;
    }
    mxn=n;//如果最长不降子序列只有一个元素就输出最后一个元素 
    for(int i=2;i<=n;i++)
        for(int j=i-1;j>=1;j--)
            if(a[i]>=a[j]&&b[j]>=b[i])
            {
                b[i]=b[j]+1;
                if(mx<=b[i])mx=b[i],mxn=i;
                c[i]=j;
            }
    cout<<"max="<<mx<<endl;
    if(mx>1)pr(c[mxn]);//本可以直接pr(mxn),但最后会多一个空格 
    cout<<a[mxn];//为避免最后多一个空格,最后一个元素单独输出 
    return 0;
}
View Code
这种方法的时间复杂度是O(n^2)级,如果n较大,容易超时,可以用二分法搜索回以优化,可以把时间复杂度做到O(nlogn),现提供“之江学院石”的朋友的代码
#include<bits/stdc++.h>
using namespace std;
int a[201], b[201], mx = 1, mxn, n;
int main()
{
    cin >> n >> a[0];
    b[0] = a[0];
    for(int i = 1; i < n; i++)
    {
        cin >> a[i];
        int j = upper_bound(b, b+mx, a[i]) - b;
        if(j == mx)
            mx++, mxn = i;
        b[j] = a[i];
    }
    for(int i = mx-2, j = mxn-1; i >= 0; i--)
    {
        for(; a[j] < b[i] || a[j] > b[i+1]; j--);
        b[i] = a[j--];
    }
    printf("max=%d
", mx);
    for(int i = 0; i < mx; i++)
        printf("%d%c", b[i], i == mx - 1 ? '
' : ' ');
}
View Code



如有不妥之处,请指教!
 
原文地址:https://www.cnblogs.com/wendcn/p/10473474.html