zoukankan      html  css  js  c++  java
  • 51nod 2500 后面第一个大于 主要方法单调栈,也讲了单调栈,要看栈的基本操作再见了

    题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=2500


    对准备看这个的同学们说一句忠告:

    1、如果你们想看这道题的枚举办法,再见~。

       我们这里没有讲这个

    2、如果你连栈是啥都不知道,再见~

       我们这里也没有讲这个

    3、如果你知道单调栈,不知道关系的话,欢迎加入群聊~

       你可以看,讲了

    4、你知道栈,不知道单调栈,欢迎加入群聊~

          你可以看,讲了

    5、如果你上面4种方式都不是,看完请评论

       请帮忙评论,说出你是什么情况

    一、题目描述

    小b有一个长度为n的序列t,现在她对于每个i,求最小的正数j满足 i + j ≤ n 且 t[i+j] > t[i],输出 j,如果不存在这样的 j,则输出0。

    样例解释:

    对于 i = 1,t[2] > t[i],所以最小的 j = 1

    对于 i = 7,不存在这样的j,所以输出0

    题意简化:给定序列,问序列中每个数后面第一个比它大的在哪,距离t[i]多远。

    不要看上面那一大段,都没用

    二、解题思路

    这道题看上去只用枚举大法就可以了,实际上这道题是单调栈。

    现在来科普一下什么是单调栈。要相信一开始弄了3天没弄懂的我,应该(注意是应该,不是肯定)不会让大家失望

    单调栈

    栈结构是一种先进后出的数据结构,单调栈是在栈的基础上实现的一种数据结构。单调栈就是栈内元素
    单调递增或者单调递减的栈。

    若是单调递增栈,则从栈顶到栈底的元素是严格递增的。若是单调递减栈,则从栈顶到栈底的元素是严
    格递减的。

    重要的部分来了~

    我相信你们看上面已经看傻了,(没办法啊,这是书上的定义,得给大家看官方定义嘛)

    总之,下面很重要

    元素进栈过程:对于单调递增栈,若当前进栈元素为 e,从栈顶开始遍历元素,把小于e或者等于e的
    元素弹出栈,直接遇到一个大于e的元素或者栈为空为止,然后再把e压入栈中。对于单调递减栈,则
    每次弹出的是大于e或者等于e的元素。

    明白了单调栈的加入元素的过程后,我们来看看它的性质,以及为啥要用单调栈。单调栈的一大优势就
    是时间复杂度为O(n)的。

    以3、4、2、6、4、5、2、3为例:

    第几步 操作                                    站内元素                
    1 3进栈 3
    2 3出栈,4进栈 4
    3 2进栈 4,2
    4 2、4出栈,6进栈 6
    5 4进栈 6,4
    6 4出栈,5进栈 6,5
    7 2进栈 6,5,2
    8 2出栈 6,5,3

    现在我们明白什么是单调栈了,这道题跟这个有什么关系呢?

    关系来了:

    因为要找的是每个数后面第一个比它大的,所以考虑倒着做。维护一个单调减的单调栈(越靠栈顶值越
    小),插入当前这个数的时候不断从栈顶弹出那些小于等于它的,直到遇见一个比它大的,那么这个数
    就是“它后面第一个比它大的了”。

    我们不妨考虑一下,这样操作为什么是合理的。如果一个数既小又靠后,那么这个数是没有什么意义
    的,因此被踢掉不会影响正确性。

    到这里大家应该都明白了吧,献上代码

    三、代码答案

    思路一:枚举大法(不推荐,毕竟人家都讲了这么久了最后你们还是用暴力。选择这个思路会使我心情失落的!所以大家不要选)

    但还是线上代码吧,作为参考,但我就懒得加注释了

    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    int main(){
        int n, t[30010];
        bool flag = 0;
        cin >> n;
        for(int i = 1;i <= n;i++){
            cin >> t[i];
        }
        for(int i = 1;i <= n;i++){
            for(int j = 1;i+j <= n;j++){
                if(t[i+j] > t[i]){
                    cout << j << ' ';
                    flag = 1;
                    break;
                }
                if(i+j == n){
                    cout << 0 << ' ';
                    flag = 1;
                    break;
                }
            }
            flag = 0;
            if(flag == 0 && i == n){
                cout << 0 << ' ';
            }
        }
        return 0;
    }

    思路二(特别推荐使用,也不是“特别”吧,但是比起暴力更推荐)

    这个给大家加上注释吧,毕竟肯定会有看上面看烦了直接看下面的同学,但是还是建议先看思路可能要不然看不懂

    #include<stack>
    #include<cstdio>
    #include<iostream>
    using namespace std;
    long long n, a[30010], ans[30010];
    stack <int> s;
    
    /*
    9
    5 2 3 1 7 6 4 5 6 
    
    8
    73 74 75 71 69 72 76 73
    */ 
    
    int main(){
        cin >> n;
        for(int i = 1;i <= n;i++){
            cin >> a[i];
        }
        for(int i = n;i >= 1;i--){//大家注意这里是从n开始的 
            //如果s里面还有数
            //并且我后面的数(上一个存在s里的)比现在这个大
            //说明现在这个的答案就是我已经存在s里的了
            //而且还说明现在这个a[i]没有用,可以踢出去 
            while(!s.empty() && a[s.top()] <= a[i]){//如果 
                s.pop();//踢出a[i] 
            }
            if(s.empty()){//如果s里面没有数字了 
                ans[i] = 0;//这个数没有答案,没有数比他大
                //所有数都被刚才淘汰掉了,或者这是a[n] 
            }else{//如果s里面还是有数字的 
                ans[i] = s.top() - i;//往答案里面存数字
                //因为题目要求我们求的是距离,所以我在s里面存的也是下标
                //只要拿上一个存在s里的数的下标-这次的下标就可以了 
            }
            s.push(i);//s里面存的是下标哦集美们  
        }
        for(int i = 1;i <= n;i++){
            cout << ans[i] << ' ';//输出答案 
        }
        return 0;
    }
  • 相关阅读:
    java调用oracle存储过程
    高富帅屌丝买房差异大 看看都该买什么房
    c#控制IE浏览器自动点击等事件WebBrowser,mshtml.IHTMLDocument2
    .net 实现微信公众平台的主动推送信息
    HTML+CSS面试知识点分享(一)
    JS面试知识点分享(一)
    HTML+CSS面试知识点分享(二)
    jQuery选择器学习理解
    PB程序系统错误处理记录
    sql server2000 直接读取excel2003文件的sheet
  • 原文地址:https://www.cnblogs.com/elisa02/p/13221984.html
Copyright © 2011-2022 走看看