NO.76十六届蓝桥杯备战|数据结构-单调栈|发射站|Largest Rectangle in a Histogram(C++)

  1. 什么是单调栈?
    单调栈,顾名思义,就是具有单调性的栈。它依旧是⼀个栈结构,只不过⾥⾯存储的数据是递增或者递减的。这种结构是很容易实现的(如下⾯的代码),但重点是维护⼀个单调栈的意义是什么
c++ 复制代码
#include <iostream>  
#include <stack>  
using namespace std;  
const int N = 3e6 + 10;  
int a[N], n;  
void test1()  
{  
	stack<int> st; // 维护⼀个单调递增的栈  
	for(int i = 1; i <= n; i++)  
	{  
		// 栈⾥⾯⼤于等于 a[i] 的元素全部出栈  
		while(st.size() && st.top() >= a[i]) st.pop();  
		st.push(a[i]);  
	}  
}  
void test2()  
{  
	stack<int> st; // 维护⼀个单调递减的栈  
	for(int i = 1; i <= n; i++)  
	{  
		// 栈⾥⾯⼩于等于 a[i] 的元素全部出栈  
		while(st.size() && st.top() <= a[i]) st.pop();  
		st.push(a[i]);  
	}  
}
  1. 单调栈解决的问题
    单调栈能帮助我们解决以下四个问题:
  • 寻找当前元素左侧,离它最近,并且⽐它⼤的元素在哪;
  • 寻找当前元素左侧,离它最近,并且⽐它⼩的元素在哪;
  • 寻找当前元素右侧,离它最近,并且⽐它⼤的元素在哪;
  • 寻找当前元素右侧,离它最近,并且⽐它⼩的元素在哪。
    虽然是四个问题,但是原理是⼀致的。因此,只要解决⼀个,举⼀反三就可以解决剩下的⼏个
  1. 寻找当前元素左侧,离它最近,并且⽐它⼤的元素在哪
    从左往右遍历元素,构造⼀个单调递减的栈。插⼊当前位置的元素的时:
  • 如果栈为空,则左侧不存在⽐当前元素⼤的元素;
  • 如果栈⾮空,插⼊当前位置元素时的栈顶元素就是所找的元素。
    注意,因为我们要找的是最终结果的位置。因此,栈⾥⾯存的是每个元素的下标
c++ 复制代码
输⼊:  
9 1  
4 10 6 3 3 15 21 8  
输出:  
0 0 0 3 4 4 0 0 8
c++ 复制代码
#include <iostream>  
#include <stack>  
using namespace std;  
const int N = 3e6 + 10;  
int a[N], n;  
int ret[N];  
void test()  
{  
	stack<int> st; // 维护⼀个单调递减的栈
	for(int i = 1; i <= n; i++)  
	{  
		// 栈⾥⾯⼩于等于 a[i] 的元素全部出栈  
		while(st.size() && a[st.top()] <= a[i]) st.pop();  
		// 此时栈顶元素存在,栈顶元素就是所求结果  
		if(st.size()) ret[i] = st.top();  
		st.push(i); // 存的是下标  
	}  
	
	for(int i = 1; i <= n; i++)  
	{  
		cout << ret[i] << " ";  
	}  
	cout << endl;  
}  

int main()  
{  
	cin >> n;  
	
	for(int i = 1; i <= n; i++) cin >> a[i];  
	test(); cout << endl;  
	
	return 0;  
}
  1. 寻找当前元素左侧,离它最近,并且⽐它⼩的元素在哪
    从左往右遍历元素,构造⼀个单调递增的栈。插⼊当前位置的元素的时:
  • 如果栈为空,则左侧不存在⽐当前元素⼩的元素;
  • 如果栈⾮空,插⼊当前位置元素时的栈顶元素就是所找的元素。
    注意,因为我们要找的是最终结果的位置。因此,栈⾥⾯存的是每个元素的下标
c++ 复制代码
输⼊:  
9 1  
4 10 6 3 3 15 21 8
输出:  
0 1 2 2 1 1 6 7 6
c++ 复制代码
#include <iostream>  
#include <stack>  
using namespace std;  
const int N = 3e6 + 10;  
int a[N], n;  
int ret[N];  
void test()  
{  
	stack<int> st; // 维护⼀个单调递增的栈  
	for(int i = 1; i <= n; i++)  
	{  
		// 栈⾥⾯⼤于等于 a[i] 的元素全部出栈  
		while(st.size() && a[st.top()] >= a[i]) st.pop();  
		// 此时栈顶元素存在,栈顶元素就是所求结果  
		if(st.size()) ret[i] = st.top();  
		st.push(i); // 存的是下标  
	}  
	for(int i = 1; i <= n; i++)  
	{  
		cout << ret[i] << " ";  
	}  
	cout << endl;  
}  

int main()  
{  
	cin >> n;  
	for(int i = 1; i <= n; i++) cin >> a[i];  
	test(); cout << endl;
	return 0;  
}

针对其余两种情况,我们仅需逆序遍历数组即可。

  1. 寻找当前元素右侧,离它最近,并且⽐它⼤的元素在哪

从右往左遍历元素,构造⼀个单调递减的栈。插⼊当前位置的元素的时:

  • 如果栈为空,则左侧不存在⽐当前元素⼤的元素;
  • 如果栈⾮空,插⼊当前位置元素时的栈顶元素就是所找的元素。
    注意,因为我们要找的是最终结果的位置。因此,栈⾥⾯存的是每个元素的下标
c++ 复制代码
输⼊:  
9 1  
4 10 6 3 3 15 21 8  
输出:  
2 3 7 7 7 7 8 0 0
c++ 复制代码
#include <iostream>  
#include <stack>  
using namespace std;  
const int N = 3e6 + 10;  
int a[N], n;  
int ret[N];  
void test()  
{  
	stack<int> st; // 维护⼀个单调递减的栈
	for(int i = n; i >= 1; i--)  
	{  
		// 栈⾥⾯⼩于等于 a[i] 的元素全部出栈  
		while(st.size() && a[st.top()] <= a[i]) st.pop();  
		// 此时栈顶元素存在,栈顶元素就是所求结果  
		if(st.size()) ret[i] = st.top();  
		st.push(i); // 存的是下标  
	}  
	for(int i = 1; i <= n; i++)  
	{  
		cout << ret[i] << " ";  
	}  
	cout << endl;  
}  
int main()  
{  
	cin >> n;  
	for(int i = 1; i <= n; i++) cin >> a[i];  
	test(); cout << endl;  
	return 0;  
}
  1. 寻找当前元素右侧,离它最近,并且⽐它⼩的元素在哪
    从右往左遍历元素,构造⼀个单调递增的栈。插⼊当前位置的元素的时:
  • 如果栈为空,则左侧不存在⽐当前元素⼩的元素;
  • 如果栈⾮空,插⼊当前位置元素时的栈顶元素就是所找的元素。
    注意,因为我们要找的是最终结果的位置。因此,栈⾥⾯存的是每个元素的下标
c++ 复制代码
输⼊:  
9 1  
4 10 6 3 3 15 21 8
输出:  
0 5 4 5 0 0 9 9 0
c++ 复制代码
#include <iostream>  
#include <stack>  
using namespace std;  
const int N = 3e6 + 10;  
int a[N], n;  
int ret[N];  
void test()  
{  
	stack<int> st; // 维护⼀个单调递增的栈  
	for(int i = n; i >= 1; i--)  
	{  
		// 栈⾥⾯⼤于等于 a[i] 的元素全部出栈  
		while(st.size() && a[st.top()] >= a[i]) st.pop();  
		// 此时栈顶元素存在,栈顶元素就是所求结果  
		if(st.size()) ret[i] = st.top();  
		st.push(i); // 存的是下标  
	}  
	for(int i = 1; i <= n; i++)  
	{  
		cout << ret[i] << " ";  
	}  
	cout << endl;  
}  

int main()  
{  
	cin >> n;  
	for(int i = 1; i <= n; i++) cin >> a[i];  
	test(); cout << endl;
	return 0;  
}
  • 找左侧,正遍历;找右侧,逆遍历;
  • ⽐它⼤,单调减;⽐它⼩,单调增。
P5788 【模板】单调栈 - 洛谷

右侧离它最近并且⽐它⼤的元素:

  • 逆序遍历数组;
  • 构造⼀个单调递减的栈;
  • 进栈时,栈顶元素就是最终结果
c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 3e6 + 10;

int n;
int a[N];
int ret[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    stack<int> st;
    for (int i = n; i >= 1; i--)
    {
        while (st.size() && a[st.top()] <= a[i]) st.pop();

        if (st.size()) ret[i] = st.top();
        st.push(i);
    }

    for (int i = 1; i <= n; i++) cout << ret[i] << " ";
    cout << endl;
    
    return 0;
}
P1901 发射站 - 洛谷
c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1e6 + 10;

int n;
LL h[N], v[N];
LL sum[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> h[i] >> v[i];

    //找左边
    stack<int> st;
    for (int i = 1; i <= n; i++)
    {
        //递减栈
        while (st.size() && h[st.top()] <= h[i]) st.pop();
        
        if (st.size())
        {
            sum[st.top()] += v[i];
        }
        st.push(i);
    }

    //找右边
    while (st.size()) st.pop(); //清空
    for (int i = n; i >= 1; i--)
    {
        //递减栈
        while (st.size() && h[st.top()] <= h[i]) st.pop();
        
        if (st.size())
        {
            sum[st.top()] += v[i];
        }
        st.push(i);
    }

    LL ret = 0;
    for (int i = 1; i <= n; i++) ret = max(ret, sum[i]);

    cout << ret << endl;
    
    return 0;
}
SP1805 HISTOGRA - Largest Rectangle in a Histogram - 洛谷

对于x位置⼦矩阵,找到左侧离它最近并且⽐它⼩的位置y ,那么[x+1, y]之间就是该矩阵能到达的左端。

同理再找到右侧离它最近并且⽐它⼩的位置z ,那么[y, z - 1]之间就是该矩阵能到达的右端。

对于每⼀个⼦矩阵,求出它向左以及向右能延伸的最⼤⻓度即可

c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1e5 + 10;

int n;
LL h[N];
LL x[N], y[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    while (cin >> n, n)
    {
        for (int i = 1; i <= n; i++) cin >> h[i];

        //找左边
        stack<int> st;
        for (int i = 1; i <= n; i++)
        {
            //单调递增栈
            while (st.size() && h[st.top()] >= h[i]) st.pop();

            if (st.size()) x[i] = st.top();
            else x[i] = 0;

            st.push(i);
        }

        //找右边
        while (st.size()) st.pop();

        for (int i = n; i >= 1; i--)
        {
            //单调递增栈
            while (st.size() && h[st.top()] >= h[i]) st.pop();

            if (st.size()) y[i] = st.top();
            else y[i] = n + 1;

            st.push(i);
        }

        LL ret = 0;
        for (int i = 1; i <= n; i++)
        {
            ret = max(ret, h[i] * (y[i] - x[i] - 1));        
        }
        
        cout << ret << endl;
    }
    
    return 0;
}
相关推荐
铉铉这波能秀5 小时前
LeetCode Hot100 中 enumerate 函数的妙用(2026.2月版)
数据结构·python·算法·leetcode·职场和发展·开发
墨有6665 小时前
哈希表从入门到实现,一篇吃透!
数据结构·算法·哈希算法
Yu_Lijing5 小时前
网络复习篇——网络基础(一)
网络·c++·笔记
Bella的成长园地5 小时前
为什么c++中的条件变量的 wait() 函数需要配合while 循环或谓词?
c++·面试
charlee445 小时前
为什么现代 C++ 库都用 PIMPL?一场关于封装、依赖与安全的演进
c++·智能指针·raii·pimpl·编译防火墙·封装设计
啊阿狸不会拉杆5 小时前
《机器学习导论》第 7 章-聚类
数据结构·人工智能·python·算法·机器学习·数据挖掘·聚类
MSTcheng.5 小时前
CANN ops-math算子的跨平台适配与硬件抽象层设计
c++·mfc
code monkey.5 小时前
【Linux之旅】Linux 进程间通信(IPC)全解析:从管道到共享内存,吃透进程协作核心
linux·c++·ipc
薛定谔的猫喵喵6 小时前
基于C++ Qt的唐代诗歌查询系统设计与实现
c++·qt·sqlite
Re.不晚6 小时前
JAVA进阶之路——数据结构之线性表(顺序表、链表)
java·数据结构·链表