文章目录

  • 什么是单调栈
    • 模拟实例:
  • 一、【模板】单调栈
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 题目解析:
  • 二、发射站
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 解题思路:
  • 三、双子序列最大和
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 样例 #2
      • 样例输入 #2
      • 样例输出 #2
    • 提示
    • 解题思路:

什么是单调栈

单调栈就是单调的栈

(艹这不是废话吗 )

单调栈维护的就是一个数前/后第一个大于/小于他的数。

这样说来好像没啥,那就让我们来个具体的实例体验一下吧:

模拟实例:

有个集训队招人,一个数代表了一个选手的能力值,先进来的选手年龄会比较大,后面的选手年龄比较小,但是这个集训队没有人数限制,那么如果遇到一个比你小还比你强的人那么准备退役吧。

比如有 5 个能力值分别是:

1 7 5 6 3

要加进来这个单调栈。

首先是 1 也就是 选手1 ,那么集训队没有人和他比较所以进入集训队。

单调栈的情况:1 。

然后是 7 也就是 选手2 ,那么我们可以发现,选手2 比 选手1 要小,并且 选手2 的能力比 选手1 要强,那么 选手1 留着也没啥用,淘汰!好残酷呀!

单调栈的情况:7 。

然后是 5 选手3 ,选手3 比 选手2 年龄要小,但是 选手3 的能力没有 选手2 强,那么 选手3 招进集训队。

单调栈的情况:7 5 。

那么接下来是 6 选手4 ,选手4 比 选手3 年龄要小,比他还要强, 选手3 淘汰!往后比较,发现 选手2 虽然比 选手4 要大,但是他能力很强!所以不会被淘汰,将 选手4 招进集训队。

单调栈的情况:7 6 。

最后是 3 选手5 ,选手5 比 选手4 要小,但是 选手4 的能力比 选手5 要强,所以 选手4 必定也比不过 选手2 ,但由于 选手5 小所以不淘汰他。

单调栈的情况:7 6 3 。

通过这个模拟我们发现,如果你很强,就算你年龄大也不会被淘汰,其实不是这样,在单调队列(比单调栈高级的东西)里面你就算再强也终究有时候会退役的,所以好好珍惜吧!

一、【模板】单调栈

题目描述

给出项数为 nnn 的整数数列 a1…na_{1 \dots n}a1…n​。

定义函数 f(i)f(i)f(i) 代表数列中第 iii 个元素之后第一个大于 aia_iai​ 的元素的下标,即 f(i)=min⁡i<j≤n,aj>ai{j}f(i)=\min_{i<j\leq n, a_j > a_i} \{j\}f(i)=mini<j≤n,aj​>ai​​{j}。若不存在,则 f(i)=0f(i)=0f(i)=0。

试求出 f(1…n)f(1\dots n)f(1…n)。

输入格式

第一行一个正整数 nnn。

第二行 nnn 个正整数 a1…na_{1\dots n}a1…n​。

输出格式

一行 nnn 个整数 f(1…n)f(1\dots n)f(1…n) 的值。

样例 #1

样例输入 #1

5
1 4 2 3 5

样例输出 #1

2 5 4 5 0

提示

【数据规模与约定】

对于 30%30\%30% 的数据,n≤100n\leq 100n≤100;

对于 60%60\%60% 的数据,n≤5×103n\leq 5 \times 10^3n≤5×103 ;

对于 100%100\%100% 的数据,1≤n≤3×1061 \le n\leq 3\times 10^61≤n≤3×106,1≤ai≤1091\leq a_i\leq 10^91≤ai​≤109。

题目解析:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int maxn=3e6+5;
int a[maxn];
int f[maxn];//f(i) 代表数列中第 i 个元素之后第一个大于 ai 的下标
stack<int> s;//存的是元素的下标
//单调栈
void solve(){int n;cin >> n;for (int i = 1; i <= n; i++)cin >> a[i];for (int i = n; i >= 1; i--){//从后往前扫描while (!s.empty() && a[s.top()] <= a[i])//栈顶元素小于当前这个数s.pop();//出栈f[i] = s.empty() ? 0 : s.top();//存入第一个大于的位置s.push(i);//当前元素入栈}for (int i = 1; i <= n; i++)cout << f[i] << " ";
}int main(){//ios::sync_with_stdio(0);solve();return 0;
}

二、发射站

题目描述

某地有 NNN 个能量发射站排成一行,每个发射站 iii 都有不相同的高度 HiH_iHi​,并能向两边(两端的发射站只能向一边)同时发射能量值为 ViV_iVi​ 的能量,发出的能量只被两边最近的且比它高的发射站接收。显然,每个发射站发来的能量有可能被 000 或 111 或 222 个其他发射站所接受。

请计算出接收最多能量的发射站接收的能量是多少。

输入格式

第 111 行一个整数 NNN。

第 222 到 N+1N+1N+1 行,第 i+1i+1i+1 行有两个整数 HiH_iHi​ 和 ViV_iVi​,表示第 iii 个人发射站的高度和发射的能量值。

输出格式

输出仅一行,表示接收最多能量的发射站接收到的能量值。答案不超过 32 位带符号整数的表示范围。

样例 #1

样例输入 #1

3
4 2
3 5
6 10

样例输出 #1

7

提示

对于 40%40\%40% 的数据,1≤N≤5000,1≤Hi≤105,1≤Vi≤1041\le N\le 5000,1\le H_i\le 10^5,1\le V_i\le 10^41≤N≤5000,1≤Hi​≤105,1≤Vi​≤104。

对于 70%70\%70% 的数据,1≤N≤105,1≤Hi≤2×109,1≤Vi≤1041\le N\le 10^5,1\le H_i\le 2\times 10^9,1\le V_i\le 10^41≤N≤105,1≤Hi​≤2×109,1≤Vi​≤104。

对于 100%100\%100% 的数据,1≤N≤106,1≤Hi≤2×109,1≤Vi≤1041\le N\le 10^6,1\le H_i\le 2\times 10^9,1\le V_i\le 10^41≤N≤106,1≤Hi​≤2×109,1≤Vi​≤104。

解题思路:

首先发出的能量只被两边最近的且比它高的发射站接收。

所以我们需要找到左右两边比当前高的最近的位置

考虑一下:

一个塔右端有大于他的塔,就等同于右端的一个塔的左端有一个小于它的塔。

每次维护单调栈的过程,就是寻找左端小于它的塔的过程。

然后我们用一个单调栈:当前位置左端的一个单调递减栈

一组塔的高度: 7 6 2 1 3 5 8

则当 i 塔为3

单调栈内:

栈顶 1
2
6
栈底 7

然后我们判断h[st[top]] < h[i]

单调栈会比变为:

栈顶 6
栈底 7

即 弹出来了 12 ,所以弹出的塔就刚好是塔 3 左端小于 3 的塔

换句话说,塔 3 就是 12 右端最近的比他们高的塔

所以更新塔 3 接收的能量 sum[i] += v[st[top--]];

while 停止的时候 ,也就找到了塔 3 左端最近的大于它的塔 6

6 接收塔 3 的能量:sum[st[top]] += v[i];

然后塔 3 入栈:

栈顶 3
6
栈底 7
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pair<int,int>> vpii;
#define all(a) a.begin(), a.end()
#define nl '\n'
#define debug() cout << "debug:"
const int inf = 0x3f3f3f3f;
const int maxn = 2e5+5;
//只被两边最近的且比它高的发射站接收
int n;
int h[maxn],v[maxn];
int st[maxn];//单调栈维护高度的单调性
int sum[maxn];//每个下标i接收的能量和
int top;
void solve(){cin >> n;for (int i = 1; i <= n; i++){cin >> h[i] >> v[i];while (top && h[st[top]] < h[i])//比i矮的都出栈sum[i] += v[st[top--]];//栈顶的元素可以给当前的i传递能量sum[st[top]] += v[i];//当前的i给栈顶的元素传递能量st[++top] = i;//入栈}int ans = 0;for (int i = 1; i <= n; i++)ans = max(ans, sum[i]);//更新最值cout << ans << nl;
}int main(){//ios::sync_with_stdio(0);//freopen("in", "r", stdin);int t = 1;//cin>>t;while(t--)solve();return 0;
}

三、双子序列最大和

题目描述

给定一个长度为n的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出最大和。一个连续子序列的和为该子序列中所有数之和。每个连续子序列的最小长度为1,并且两个连续子序列之间至少间隔一个数。

输入格式

第一行是一个整数表示n。

第二行是n个整数表示整数序列。

输出格式

一个数,两个连续子序列的序列和之和。

样例 #1

样例输入 #1

5
83 223 -13 1331 -935

样例输出 #1

1637

样例 #2

样例输入 #2

3
83 223 -13

样例输出 #2

70

提示

对于30%的数据N<=100。

对于60%的数据有N<=10000。

对于100%的数据有N<=1000000。

数据保证运算过程不会超过long long(int64)。

解题思路:

给出一段数列,求出连续的非空的字段,使得和最大。

那么,我们可以令 fif_ifi​ 表示以 iii 结尾的最大子串,那么,若 fi−1f_{i-1}fi−1​ 大于 000,则拼在一起比较划算,否则就单独出来.也就是:
fi=max(fi−1+ai,ai)f_{i}=max(f_{i-1}+a_{i},a_{i})fi​=max(fi−1​+ai​,ai​)
或者说
fi=max(fi−1,0)+aif_{i}=max(f_{i-1},0)+a_{i}fi​=max(fi−1​,0)+ai​
其核心代码如下:

for(int i=0;i<n;i++){scanf("%d",&x);f[i]=max(f[i-i],0)+a[i];
}

然后我们便可以考虑如何从此题中更深一步得到这道题的解法。

我们仍然可以令 gig_igi​ 表示以 iii 结尾的两个子串的权值最大值,那么,gig_igi​ 有两种方法可以得到。

1.由 gi−1g_{i-1}gi−1​继承而来,这样与上面是类似的。但与之不同的是,因为要有两个子串,所以前面的我们不能直接舍掉,即不能取 000,所以整个式子就是:
gi=gi−1+aig_{i}=g_{i-1}+a_{i}gi​=gi−1​+ai​
2.由之前的一个子串得来,也就是:
gi=max⁡j<i−1fj+aig_{i}=\max_{j<i-1}f_j+a_{i}gi​=j<i−1max​fj​+ai​
综合来讲,就是
gi=max⁡(gi−1,max⁡j<i−1fj)+aig_{i}=\max(g_{i-1},\max_{j<i-1}f_j)+a_{i}gi​=max(gi−1​,j<i−1max​fj​)+ai​

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pair<int,int>> vpii;
#define all(a) a.begin(), a.end()
#define nl '\n'
#define debug() cout << "debug:"
const int inf = 0x3f3f3f3f;
const int maxn = 2e6+5;
//给定一个长度为n的整数序列,要求从中选出两个连续子序列,
//使得这两个连续子序列的序列和之和最大,最终只需输出最大和。
ll one[maxn], two[maxn];
ll ans = -1e18;//记录答案
ll maxx = -1e18;//记录最大的one[]
void solve(){ll n;cin >> n;one[0] = -1e18;for (int i = 1; i <= n; i++){ll x;cin >> x;one[i] = max(one[i - 1], 0ll) + x;maxx = max(maxx, one[i - 1]);two[i] = max(two[i - 1], maxx) + x;if(i>2)//选取两个子串,长度要大于2ans = max(ans, two[i]);}cout << ans << nl;
}
int main(){//ios::sync_with_stdio(0);//freopen("in", "r", stdin);int t = 1;//cin>>t;while(t--)solve();return 0;
}

【单调栈】【概念讲解模板代码】相关推荐

  1. 单调栈之Next Greater Number

    单调栈之Next Greater Number 栈(stack) 是很简单的⼀种数据结构, 先进后出的逻辑顺序, 符合某些问 题的特点, ⽐如说函数调⽤栈. 单调栈实际上就是栈, 只是利⽤了⼀些巧妙的 ...

  2. 【数据结构】单调栈和单调队列 详解+例题剖析

    算法:单调栈和单调队列 一.单调栈和单调队列 二.单调栈例题 1.模板题入门 2.不懂不要急,看这道题 三.单调队列例题 1.入门 2.进阶 一.单调栈和单调队列 单调栈和单调队列与普通的栈,队列不同 ...

  3. LNSYOJ#204谁是天才【RMQ理解+单调栈】【做题报告】

    这是一道RMQ水题,因为本蒟蒻居然一A 题目描述 张大牛:"我是天才!" 大肥熊:"你为什么是天才?" 张大牛:"你随便给我一个单词(大小写字母组成) ...

  4. 【POJ - 3250 】Bad Hair Day (单调栈)

    题干: Some of Farmer John's N cows (1 ≤ N ≤ 80,000) are having a bad hair day! Since each cow is self- ...

  5. 20191024:单调栈问题的引出

    单调栈问题的引出 题目 代码实现 题目 Leetcode习题1124:表现良好的最长时间段 做上题时自己暴力的做法会超时,但是时间不允许继续往下探究,明天完成单调栈的探究. 代码实现 1124题暴力解 ...

  6. 牛客 小米校招 最大新整数 单调栈

    题目描述 有一十进制正整数,移除其中的 K 个数,使剩下的数字是所有可能中最大的. 假设: 字符串的长度一定大于等于 K 字符串不会以 0 开头 输入描述: 一行由正整数组成的数字字符串,和一个正整数 ...

  7. [jzoj 6305] 最小值 {单调栈}

    题目 解题思路 g[i]g[i]g[i]表示1到n已经划分完毕的最大价值和. O(n2)O(n^2)O(n2)方程很容易想到. 我们可以用单调栈来维护. 代码 #include<cstdio&g ...

  8. 模板_单调栈_AcWing_830. 单调栈_底顶递增栈

    模板_单调栈_AcWing_830. 单调栈_底顶递增栈 830. 单调栈 题目 提交记录 讨论 题解 视频讲解 给定一个长度为 NN 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 − ...

  9. 差异:后缀数组(wzz模板理解),单调栈

    因为涉及到对模板的理解,所以就着代码看会好一些. 让那些坚决不颓代码的人受委屈了. 我是对着wzz的板子默写的,可能不完全一样啊. 还有代码注释里都是我个人的理解,不保证正确,但欢迎指正. 可以有选择 ...

最新文章

  1. 清理SQL Server日志释放文件空间的终极方法
  2. getopt:命令行选项、参数处理
  3. vs debug 模式生成的exe 另一台电脑_神秘的 _DEBUG 宏从何处来?
  4. NanShan企业即时通讯开始写博客
  5. 应用zip压缩的javascript以及Egret H5游戏实战
  6. python 员工考勤_用python写的考勤自动打卡程序
  7. 测试ai模糊软件,“马赛克”视频能被AI软件彻底还原?测试结果出乎意料!
  8. Arcgis 镶嵌栅格报错999999,且生成x3569458.tif文件
  9. 通过链接下载google drive文件
  10. 转载双显示器显示模式介绍
  11. 深度学习第二次培训(BP神经网络)
  12. 一个在国内外使用广泛的精密电阻品牌介绍
  13. 华为mate9安装Fiddler证书
  14. 创宇蜜罐入驻华为严选商城,与华为云共同构建积极纵深防御体系
  15. FootStep 6、诺基亚、摩托罗拉、索尼爱立信和三星手机系列命名介绍『整理、了解』
  16. iphonex 序列号_iPhoneX序列号在哪?苹果iPhoneX序列号怎么看?
  17. java使用axis2调用webservice接口实例
  18. powershell导入脚本失败,禁止运行脚本,无法远程连接服务器
  19. 分布式之数据库和缓存双写一致性方案解析
  20. 高中关于人工智能方面的课题_《人工智能的发展与应用》课题开题报告

热门文章

  1. 数字IC-1.8 子模块组建整模块-动态数码管设计代码实例
  2. 所有伟大的创新,本质上都是从一小撮年轻人肆无忌惮打破规则开始的
  3. 判断两个字符串是否互为变位词
  4. 如何利用ASP实现邮箱访问(转)
  5. 回到顶部丝滑效果/指定位置下拉丝滑
  6. 深度剖析移动游戏市场,论产品、研发、买量
  7. animate动画执行一次并且停留在最后一帧
  8. 解决PYTHON爬取:RESPONSE.STATUS_CODE为418、403 问题
  9. Android所有动画分类总结
  10. 计算机网络系统拓扑图