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;
}
相关推荐
代码程序猿RIP3 分钟前
C++(22)—内存管理
开发语言·数据结构·c++·算法
孞㐑¥15 分钟前
C++之哈希
开发语言·c++·经验分享·笔记
奇树谦26 分钟前
C/C++语言常见问题-智能指针、多态原理
c语言·开发语言·c++
溟洵1 小时前
【C++ Qt】Hello World、初始信号槽、理解对象树 ~~~(通俗易懂 图文并茂)
开发语言·c++·qt
八股文领域大手子1 小时前
深入浅出 Redis:核心数据结构解析与应用场景Redis 数据结构
java·数据结构·数据库·人工智能·spring boot·redis·后端
haaaaaaarry2 小时前
【贪心】C++ 活动安排问题
开发语言·c++·算法·贪心
一只专注api接口开发的技术猿2 小时前
基于 Java 的淘宝 API 调用实践:商品详情页 JSON 数据结构解析与重构
大数据·数据结构·重构·json
君义_noip2 小时前
信息学奥赛一本通 1508:Easy SSSP
c++·图论·信息学奥赛
末央&2 小时前
【C++】深入浅出之继承
开发语言·c++
ChengZUOZZZ2 小时前
蓝桥杯题目:二维前缀和
java·算法·蓝桥杯