单调队列&单调栈

就是一些很神奇的数据结构

A:最大矩形

题目:

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。

input:

输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。

output:

对于每组测试数据输出一行一个整数表示答案。

在矩形条里面选取最大的矩形,首先想到暴力做法的话,估计必须会有O(n^2)的复杂度(每个点都找左右延申最远) 这样耗费不可接受。所以就要用到这里的数据结构,单调栈
所谓单调栈就是,以递增栈为例,如果加入元素小于栈顶,入栈,否则栈顶出栈,直到满足元素小于栈顶或者栈空。

对于矩形柱,从当前向右,要使得必须大于当前的矩形柱才可以延申。因此维护的是一个递增栈,是当前元素i被pop 的时候就说明这时候要加入的这个元素比当前元素小,也就是能够向右拓展的最大距离了。
向左拓展的距离类似,反向建立一个单增栈即可。
(说起来简单其实需要自己稍微模拟一下)

#include<iostream>
#include<stack>
using namespace std;
const int N = 100000 + 50;
long long maxit(long long a, long long b)
{
    if (a > b)
    {
        return a;
    }
    else
    {
        return b;
    }
}
int lf, rt;
int hight;
long long ans;
long long n, a[N], L[N], R[N], st[N];
int main()
{
    cin >> n;
    while(n!=0)
    {
        
    ans=0;
    stack<long long> A;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    int i = 0;
    while (i < n)
    {
        if (A.empty() || a[A.top()] <= a[i])
        {
            A.push(i);
            i++;
        }
        else
        {
            int tp = A.top();
            A.pop();
            long long area;
            if (A.empty())
            {
                area = a[tp] * i;
                ans = maxit(ans, area);
            }
            else
            {
                area = a[tp] * (i -1- A.top());
                ans = maxit(ans, area);
            }
        }
    }
    while (!A.empty())
    {
        long long area;
        int tp = A.top();
        A.pop();
        if (A.empty())
        {
            area = a[tp] * i;
            ans = maxit(ans, area);
        }
        else
        {
            area = a[tp] * (i -1-A.top());
            ans = maxit(ans, area);
        }
    }
    cout << ans << endl;
    cin>>n;
    //for(int i=0)
    }
    return  0;
}

直接用了stl,据说手动模拟更简单?

B:TT’s Magic Cat

题目:

给定一个数组,在其(l,r)区间内的每个值都增加k,求q轮后的数组。

input:

第一行包含两个整数n,q(1≤n,q≤2⋅105)分别表示数组的长度和操作的轮数
第二行包含序列a的元素:整数a1、a2、…、an(-106≤ai≤106)。
接下来是q行,每一行代表一个操作。第i行包含用于第i操作的三个整数l、r和c(1≤l≤r≤n,-105≤c≤105)。

output:

打印出变化后的数组

暴力:O(n^2)的时间复杂度无法接受。
所以要做的就是用新的办法。我们用的是差分法》 那么啥是差分


image.png

(这样/我知道手写不是一个好习惯orz

#include<iostream>
using namespace std;
long long a[300020];
long long b[300020];
int main()
{
    int m,n;
    cin>>n>>m;
    int lf,rt;
    for(int i=0;i<n;i++)   
    {
        scanf("%lld",&a[i]);
    }
    b[0]=a[0];
    for(int i=1;i<n;i++)   
    {
        b[i]=a[i]-a[i-1];
    }
    for(int i=0;i<m;i++)   
    {
        int value;
        scanf("%d%d%d",&lf,&rt,&value);
        b[lf-1]=b[lf-1]+value;
        b[rt]=b[rt]-value;
    }
    a[0]=b[0];
    for(int i=1;i<n;i++)   
    {
        a[i]=b[i]+a[i-1];
    }
    for(int i=0;i<n;i++)   
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    return 0;
}

C:平衡字符串

题目:

一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。

如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。

现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?

如果 s 已经平衡则输出0。

input:

一行字符表示给定的字符串s

output:

一个整数表示答案

样例输入:

QWER

样例输出:

0

样例输入:

QQWE

样例输出:

1

样例输入:

QQQE

样例输出:

2

样例输入:

QQQQ

样例输出:

3
这题要介绍的办法是,尺取法。简单说就是拿着一个游标尺子去取长度。
不符合条件,右标右移,符合条件,左标右移。当然尺取的区间需要连续。
显然这道题符合这样的条件。。那么剩下的就是进行统计了,统计区间外面的字母数量(首先每个字母该有几个我们是知道的) 然后……区间内部的字母都可以作为自由的去填补这些缺口,看看缺口能不能被补上确定是不是“符合条件”

#include<iostream>
#include<string>
#include<cstring>
#include<string.h>
using namespace std;
int a[4];
int ziyouji=0;
int bl(char c)
{
    if(c=='Q') return 0;
    else if(c=='W') return 1;
    else if(c=='E') return 2;
    else if(c=='R') return 3;
}
int minit(int a,int b)
{
    if(a<b) return a;
    else return b;
}
bool queren(int ll)
{
    int tx=0;
    for(int i=0;i<4;i++)
    {
        if(a[i]>ll)
        {
            return false;
        }
        tx+=ll-a[i];
    }
    if(tx==ziyouji)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
int main()
{
    int now;
    
    memset(a,0,sizeof(a));
    string x;
    cin>>x;
    int ans=x.length();
    int t=x.length();
    for(int i=0;i<t;i++)
    {
        if(x[i]=='Q') a[0]++;
        else if(x[i]=='W') a[1]++;
        else if(x[i]=='E') a[2]++;
        else if(x[i]=='R') a[3]++;
    }
    int tl=t/4;
    int l=0,r=0;
    while(r<x.length())
    {
        if(queren(tl))
        {
            //cout<<"hello"<<l<<" "<<r<<endl;
            ans=minit(ans,r-l);
            a[bl(x[l])]++;
            l++;
            ziyouji--;
        }
        else
        {
            //cout<<"hi"<<l<<" "<<r<<endl;
            a[bl(x[r])]--;
            ziyouji++;
            r++;
        }
    }

    cout<<ans<<endl;
    return 0;
}

D:滑动窗口
题目:
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.


image.png

input:
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。

output:
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。

样例输入:
8 3
1 3 -1 -3 5 3 6 7
样例输出:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
涉及到和第一题类似的数据结构,单调队列。但和单调栈不同的是单调队列是双向的。
找局部的最大值和最小值,
1.首先维护双端队列的单调性,(假设从队头到队尾递减)当前元素若比队尾元素值小,则弹出元素直到满足条件。
2.同时要维护窗口内的元素个数,如果队首的元素已经在窗口外,就把他弹出。
3.每一次循环的队首元素就是当前窗口的最大值或者是最小值。
(同样不太好理解QAQ)

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<deque>
#define MAXN 1000010
int a[MAXN];
int maxx[MAXN],minn[MAXN];
using namespace std;
int n,k;    
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
    deque<int> q;//储存下标
    for(int i=1;i<=k-1;i++)
    {   while(!q.empty()&&a[q.back()]>a[i])
            q.pop_back();
        q.push_back(i);
    }
    for(int i=k;i<=n;i++)//窗口从k开始向右移动 维护一个单增队列
    {
        while(!q.empty()&&a[q.back()]>a[i]) q.pop_back();//先维护单调性
        q.push_back(i);
        while(!q.empty()&&(i-q.front())>=k) q.pop_front();//然后维护窗口的大小
        minn[i]=q.front();
    }
    q.clear();
    for(int i=1;i<=k-1;i++)
    {   while(!q.empty()&&a[q.back()]<a[i])
            q.pop_back();
        q.push_back(i);
    }
    for(int i=k;i<=n;i++)//窗口从k开始向右移动 维护一个单增队列
    {
        while(!q.empty()&&a[q.back()]<a[i]) q.pop_back();//先维护单调性
        q.push_back(i);
        while(!q.empty()&&(i-q.front())>=k) q.pop_front();//然后维护窗口的大小
        maxx[i]=q.front();
    }

    for(int i=k;i<=n;i++)   printf("%d ",a[minn[i]]);
    cout<<endl;
    for(int i=k;i<=n;i++)   printf("%d ",a[maxx[i]]);
    cout<<endl;
    system("pause");
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,365评论 0 5
  • 说明: 本文中出现的所有算法题皆来自牛客网-剑指Offer在线编程题,在此只是作为转载和记录,用于本人学习使用,不...
    秋意思寒阅读 1,144评论 1 1
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-code.h...
    eddy_wiki阅读 9,329评论 0 30
  • 1.二维数组的查找 题目描述:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一...
    少年梦游计_3403阅读 1,152评论 0 1
  • “你怎么又把药吃差了?不是跟你说了几遍吗?这种药一天吃一次。我离开的时候已经给你吃了,你怎么又吃了一次呀?”女儿站...
    梦在雨巷阅读 411评论 2 3