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;
}
相关推荐
Lenyiin2 小时前
《 C++ 点滴漫谈: 四十 》文本的艺术:C++ 正则表达式的高效应用之道
c++·正则表达式·lenyiin
码农开荒路3 小时前
Redis底层数据结构之字典(Dict)
java·数据结构·数据库·redis
yxc_inspire4 小时前
基于Qt的app开发第十三天
c++·qt·app·tcp·面向对象
虾球xz4 小时前
CppCon 2015 学习:Concurrency TS Editor’s Report
开发语言·c++·学习
潇-xiao4 小时前
Qt 按钮类控件(Push Button 与 Radio Button)(1)
c++·qt
板鸭〈小号〉4 小时前
命名管道实现本地通信
开发语言·c++
lyh13445 小时前
【Fiddler抓取手机数据包】
数据结构
int型码农6 小时前
数据结构第八章(二)-交换排序
c语言·数据结构·算法·排序算法
YKPG6 小时前
C++学习-入门到精通【14】标准库算法
c++·学习·算法
zm6 小时前
极限复习c++
开发语言·c++