• 单调栈


    涉及任意区间 (子序列) 求最大最小值的一系列操作(可以求这个元素在那个区间是最小值 || 最大值)

    向左向右的第一个比当前元素大的值(的下标等)

    单调栈就行了

    例题:

    ZZULI  XXX

    题意:给你一串序列,要你求所有子序列的最小值之和。(n很大,无法暴力)

    思路:完美的单调栈模板

    这里简要介绍下单调栈的性质,(其他的都没用)

    单调栈的维护是 O(n) 级的时间复杂度,因为所有元素只会进入栈一次,并且出栈后再也不会进栈了。

    单调栈的性质:

    1.单调栈里的元素具有单调性

    2.元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除

    3.使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素

    求最小值用单调递增  最大值用单调递减

    (也就是说在元素进栈前他向左拓展的区间已经确定,在出栈前她能向右拓展的区间也能确定)(即这个元素在哪个区间是最小的(最大的)

    #include<map>     
    #include<stack>            
    #include<queue>            
    #include<vector>    
    #include<string>  
    #include<math.h>            
    #include<stdio.h>            
    #include<iostream>            
    #include<string.h>            
    #include<stdlib.h>    
    #include<algorithm>   
    #include<functional>    
    using namespace std;
    typedef long long  ll;
    #define inf  1000000000       
    #define mod 1000000007             
    #define maxn  286000  
    #define PI 3.1415926
    #define lowbit(x) (x&-x)            
    #define eps 1e-9  
    struct node
    {
        long long x, y, l, r;
    }str[maxn];
    stack<node>t;
    int  main()
    {
        long long  T, i, j, n, m, k, sum;
        scanf("%lld", &T);
        while (T--)
        {
            sum = 0;
            scanf("%lld", &n);
            for (i = 1;i <= n;i++)
            {
                scanf("%lld", &str[i].x);
                str[i].y = i;
            }
            for (i = 1;i <= n;i++)
            {
                str[i].l = i;
                while (t.empty() == 0 && t.top().x>str[i].x)
                {
                    str[t.top().y].r = i - 1;
                    str[i].l = str[t.top().y].l;
                    t.pop();
                }
                t.push(str[i]);
            }
            while (t.empty() == 0)
            {
                str[t.top().y].r = n;
                t.pop();
            }
            for (i = 1;i <= n;i++)
                sum += str[i].x*(i - str[i].l + 1)*(str[i].r - i + 1);
            printf("%lld
    ", sum);
        }
    }

    应用:

    以上就是一个单调栈的定义及其实现,下面就来说一下它可以解决哪些问题。其实我也不能给出证明,以证明它为什么能完成这些功能,只是简单的把它的用途说一下,碰到问题时就需要大家灵活运用了。

    1.最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。

    2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。

    3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。

    对应题目:下面先只给出对应的题目和大体思路,至于题解稍后完成后再一一补充。当然这只是现阶段我自己碰到过的,可能不全,还请大家多多补充指正。

    应用1对应题目:

    1.POJ 3250

    题意:有一群牛站成一排,每头牛都是面朝右的,每头牛可以看到他右边身高比他小的牛。给出每头牛的身高,要求每头牛能看到的牛的总数。

    思路:这也就是应用1所说的求每个数和它右边第一个比它大的数之间的数的个数,分别求出后相加即可。朴素的做法是双重循环遍历,时间复杂度为O(n^2),用单调栈为O(n)。

    题解:POJ 3250题解。

    应用2对应题目:

    1.POJ 2559

    题意:有N个矩形,宽度都为1,给出N个矩形的高度,求由这N个矩形组成的图形包含的最大的矩形面积。

    思路:可以转化为求区间最小值乘以区间长度的最大值。普通的思路是两重循环遍历,时间复杂度为O(n^2),用单调栈为O(n)。

    题解:POJ 2559 题解。

    2.POJ 3494

    题意:求仅由0,1组成的矩阵中,全部由1组成的小矩阵的最大面积。

    思路:这个是上一题POJ 2559的升级版,把一维的操作变成二维的即可。这之前需要一个预处理。

    题解:POJ 3494题解。

    应用3对应题目:

    1.POJ 2796

    题意:给出一个序列,求出一个子序列,使得这个序列中的最小值乘以这个序列的和的值最大。

    思路:直接用单调栈解决即可,由于维护单调栈的过程中会改变原数组的值,所以需要加一个sum数组保存前缀和,也方便计算区间元素和。

    题解:POJ 2796题解。

    转载:https://blog.csdn.net/haut_ykc/article/details/52249290

         https://blog.csdn.net/zuzhiang/article/details/78134247

  • 相关阅读:
    《C#并发编程经典实例》学习笔记—2.4 等待一组任务完成
    Captcha服务(后续2)— 改造Captcha服务之Asp.Net Core项目中如何集成TypeScript
    VS Code调试.Net Core版Hello World
    Visual Studio Code 语言设置
    Captcha服务(后续1)
    css——格式
    作业 5/13
    css选择器
    作业 5/12
    前端——表格标签,表单标签,css
  • 原文地址:https://www.cnblogs.com/WTSRUVF/p/10812192.html
Copyright © 2020-2023  润新知