• 求最长不下降/上升/下降/不上升子序列


    求最长不下降/上升/下降/不上升子序列

    说在前面的话

    求最长不下降/上升/下降/不上升/子序列的原理都是一样的,只是有无等于号或者大于小于号的关系,懂了一个其余的就懂了,所以我这里只拿不下降子序列来举例子吧。

    (n^2)的做法

    原理

    求最长不下降子序列的话,(i)位置应该和(i)位置之前的位置里面小于等于(i)位置上的值且最长不下降子序列最长的那个,所以可以枚举(i)位置之前的每一个点,然后按照上面说的选取就好了。

    代码

    题面详见

    #include<iostream>
    #include<cstdio>
    
    using namespace std;
    const int Max = 201;
    int n;
    int a[Max],f[Max],c[Max];
    int Ans[Max];
    
    int main()
    {
    	scanf("%d",&n); 
    	for(int i = 1;i <= n;++ i) 
    		scanf("%d",&a[i]),f[i] = 1;//输入数据将以每一个数结尾的最长不下降子序列标记为1 
    	for(int i = 1;i <= n;++ i)
    		for(int j = 1;j < i;++ j)
    			if(a[i] >= a[j])//满足最长不下降 , 也就是大于等于 
    				if(f[i] < f[j] + 1)
    					f[i] = f[j] + 1,c[i] = j;//记录 
    	int ans = 0,qwq = 1;
    	for(int i = 1;i <= n;++ i)
    		if(f[i] > ans)//找最大的 
    			ans = f[i],qwq = i;//记录最大的所在的下标 
    	printf("max=%d
    ",ans);
    	int aaa = ans;
    	while(aaa --)//到这找回去 
    	{
    		Ans[aaa + 1] = a[qwq];//用数组储存 
    		qwq = c[qwq];
    	}
    	for(int i = 1;i <= ans;++ i)//正着输出 
    		cout<<Ans[i]<<" ";
    	return 0;
    }
    

    nlogn的做法

    这才是这篇文章的重点!!!

    原理

    开一个数组(注意是额外开一个数组,不是存储读入数字串的数组),第一个数理所当然的放在这个数组的第一个位置上面,如果进来的下一个数是符合不下降条件的,那么久放在他的后面,不然就在这个序列里面找到第一个比他大的数替换掉。

    为什么要这样呢?
    看完上面应该是有一种疑惑,这样替换的话会不会影响后面的呢?答案当然是否定的。
    来说一下理由:
    如果当前数组中放了1,3,5这三个数,下一个数是2,如果放在了第一个比他大的数的位置上,那就会放在3上面,这个时候数组中存储的数据就成了1,2,5,然后当下一个数进来的时候是和5比较是不是符合不下降的条件,所以把2替换在3的位置上面并没有影响。
    具体说一下:就是如果这个数替换的那个数之后还有数,必然不会影响之后一个数拿过来之后的第一项操作,也就是先比较一下放在最后面是不是会符合不下降,那如果后面没有数了,他就是最后一个的话,用这个小的替换了前面的大的,也必然会使结果更优,所以没有问题。

    代码

    //最长不下降子序列nlogn  Song 
    
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    int a[40005];
    int d[40005];
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        if (n==0)  //0个元素特判一下 
        {
            printf("0
    ");
            return 0;
        }
        d[1]=a[1];  //初始化 
        int len=1;
        for (int i=2;i<=n;i++)
        {
            if (a[i]>=d[len]) d[++len]=a[i];  //如果可以接在len后面就接上,如果是最长上升子序列,这里变成> 
            else  //否则就找一个最该替换的替换掉 
            {
                int j=upper_bound(d+1,d+len+1,a[i])-d;  //找到第一个大于它的d的下标,如果是最长上升子序列,这里变成lower_bound 
                d[j]=a[i]; 
            }
        }
        printf("%d
    ",len);    
        return 0;
    }
    
  • 相关阅读:
    Java中怎么控制线程訪问资源的数量
    我的Hook学习笔记
    《编写可读代码的艺术》——简单总结
    Hdu 1016 Prime Ring Problem (素数环经典dfs)
    setsockopt()使用方法(參数具体说明)
    Html5培训之精髓
    [unity3d]unity平台的预处理
    音频编辑大师 3.3 注冊名 注冊码
    linux tar.gz zip 解压缩 压缩命令
    面向对象的三个基本特征
  • 原文地址:https://www.cnblogs.com/acioi/p/11837911.html
Copyright © 2020-2023  润新知