题目描述:

地上从左到右竖立着 n 块木板,从 1 到 n 依次编号,如下图所示。我们知道每块木板的高度,在第 n 块木板右侧竖立着一块高度无限大的木板,现对每块木板依次做如下的操作:对于第 i 块木板,我们从其右侧开始倒水,直到水的高度等于第 i 块木板的高度,倒入的水会淹没 ai 块木板(如果木板左右两侧水的高度大于等于木板高度即视为木板被淹没),求 n 次操作后,所有 ai 的和是多少。如图上所示,在第 4 块木板右侧倒水,可以淹没第 5 块和第 6 块一共 2 块木板,a4 = 2。

解析:

从对每块木板的操作:

对于第 i 块木板,我们从其右侧开始倒水,直到水的高度等于第 i 块木板的高度,倒入的水会淹没 ai 块木板(如果木板左右两侧水的高度大于等于木板高度即视为木板被淹没)

我们可以思考,要怎么样倒入的水才会停止"淹没" 木板呢?其实我们看图示就很清楚了,要使水不再往右延伸的话,那么必然要遇到一个比当前操作的木板更高的木板。

所以这道题转换成整型数组有n个元素,然后找到每个元素右边第一个大于该元素的数,并记录下标temp_id,那么第 i 块木板(下标为i)淹没木板的块数即为:

ai = temp_id - i - 1;    //从图示中即可计算出ai的值

如果这道题n的范围比较小的话,那么可以双重for循环直接暴力遍历即可,时间复杂度为O(n^2),但是我们仅仅就满足于这样的代码吗?有没有更好的办法能够解决这个问题?首先我们从转化后的题设入手,每个元素都要找到右边第一个大于该元素的数

那么还记得单调栈的性质3吗?单调栈的性质3:

使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。

对于这道题呢(考虑给定的测试用例),首先我们考虑从左到右依次将数组中的数据元素压入栈中,当遍历到第一个元素时,此时栈为空,所以第一个元素先要压入栈中,然后到调整部分,遍历到第二个元素,将第二个元素与栈顶相比较,如果当前遍历到的元素大于或者等于栈顶元素,栈顶元素找到了其右边第一个大于他的元素,那么这样的话,其实就已经解决了题设的问题,然后将ans(最后的结果)加上当前元素的下标 - 栈顶元素的下标 - 1,随后栈顶元素出栈(表示该栈顶元素已经处理完毕),然后重复上述过程,继续向左遍历,直到找到第一个比他大的元素,将其压入栈中,成为新的栈顶,停止遍历,处理下一个元素,而如果当前遍历到的元素小于栈顶元素,说明当前遍历到的元素无法再向左边延伸,因此将当前遍历到的元素压入栈中。

然后继续向后遍历数组中的数据元素,并按照上述方式处理当前遍历到的元素以及栈顶元素。

每次操作结束之后,仔细观察已经压入栈中的元素,你会发现,从栈底到栈顶,木板的高度始终是单调递减的。

其实这就转化成了一道利用单调栈维护的题目了,即每块遍历到的木板寻找属于“自己”的位置(包含最后一块无限长的木板),而单调栈维护的时间复杂度为O(n),所以在数据范围比较大的情况下,也不用担心程序运行时间过长的问题。

图示分析:

图示中总共有n块木板,第n+1块是一块高度为无限大的木板,按照上述思路对a1-a6这六块木板进行处理。

一开始的时候,ans赋初始值为0.

1.遍历到a1时,此时栈为空,所以将a1压入栈中,此时的栈顶元素为a1;

2.然后继续向右遍历到a2时,a2 > a1,所以此时记录第1块木板淹没木板的块数sum1 = 2 - 1 - 1 = 0,然后ans += sum1,此时的ans = 0

然后a1出栈,此时栈为空,因此a2入栈,此时新的栈顶元素为a2

3.随后遍历至a3,a3 < a2,因此a3压入栈中,成为新的栈顶。此时新的栈顶元素为a3

4.随后遍历至a4,a4 > a3,所以此时记录第3块木板淹没木板的块数sum3 = 4 - 3 - 1 = 0,然后ans += sum3,此时的ans = 0

然后a3出栈,继续向左遍历,发现a2,而a2 > a4,找到了第一个比他大的元素,停止遍历。将a4压入栈中,此时新的栈顶元素为a4

5.随后遍历至a5,a5 < a4,因此a5压入栈中,成为新的栈顶。此时新的栈顶元素为a5

6.随后遍历至a6,a6 > a5,所以此时记录第5块木板淹没木板的块数sum5 = 6 - 5 - 1 = 0,然后ans += sum5,此时的ans = 0

然后a5出栈,继续向左遍历,发现a4,而a4 > a6,找到了第一个比他大的元素,停止遍历。将a6压入栈中,此时新的栈顶元素为a6

7.然后再继续向后遍历,发现了那块高度无限大的木板,说明此时遍历结束(当前遍历到了第n+1块木板)

那么,到现在处理完毕了吗?很明显是没有的,题设要求的是 n 次操作后,所有 ai 的和是多少,而当我们遍历结束的时候,只记录了其中三块木板淹没木板的块数,而栈中从栈底到栈顶依次还有a2,a4,a6(说明这三块木板均为处理),所以呢,在for循环遍历的语句之下,还要加上一个循环判定

此时栈是否为空,而如果栈不为空,而从栈底到栈顶,木板的高度始终是单调递减的。说明当前还在栈中的数据元素,右边第一个大于该元素的数都是第n+1个数,并且第n+1个元素的位置是“栈底”,所以我们只需要从栈顶开始一一处理剩下的元素即可,直到栈空,说明全部元素处理完毕。

所以接下来是处理剩下的元素,从栈顶开始,有sum6 = 6+1-6-1 = 0,ans += sum6,此时ans = 0,a6处理完毕,出栈;

然后sum4 = 6+1-4-1 = 2,ans += sum4,此时ans = 2,a4处理完毕,出栈;

最后是a2,sum2 = 6+1-2-1 = 4,ans += sum2,此时ans = 6,a2处理完毕,出栈;

循环判断此时栈空,跳出循环,截止到此时,所有元素处理完毕。

输出结果,ans = 6.

这道单调栈的试题需要注意的是,当遍历完数组中所有的数据元素时,也就是当前遍历到的数据元素到了第n+1个,就像上图图示所示的,第n+1块木板的高度是无限大的,那么这块木板是可以“延伸”到栈底的,所以当遍历结束之后,只需要利用这块木板一一处理剩下的元素即可。

完整代码实现:

#include<iostream>
#include<cassert>
using namespace std;
class Node {
public:int id, height;
};
template<class Type> class Stack {
private:Type *urls;int max_size, top_index;
public:Stack(int length_input) {urls = new Type[length_input];max_size = length_input;top_index = -1;}~Stack() {delete[] urls;}bool push(const Type &element) {if (top_index >= max_size - 1) {return false;}top_index++;urls[top_index] = element;return true;}bool pop() {if (top_index < 0) {return false;}top_index--;return true;}Type top() {assert(top_index >= 0);return urls[top_index];}bool empty() {if (top_index < 0) {return true;} else {return false;}}
};
int main() {int n,ans = 0;cin >> n;Stack <Node> stack(n);Node temp;for(int i = 1;i <= n;i++){cin >> temp.height;temp.id = i;while(!stack.empty() && stack.top().height <= temp.height){ans = ans + i - stack.top().id - 1;stack.pop();}stack.push(temp);}while(!stack.empty()){ans = ans + n + 1 - stack.top().id - 1;stack.pop();}cout << ans << endl;return 0;
}


如有错误,还请指正,O(∩_∩)O谢谢

单调栈解木板倒水问题(单调栈的简单应用)相关推荐

  1. 单调栈解木板倒水问题

    循环操作每个木板,维护一个从栈底到栈顶单调递减的单调栈. 具体算法如下:先输入第i块木板的高度,然后标记下模板编号,记录到变量temp里.接着,temp依次和栈顶元素a比较,如果a的高度小于等于tem ...

  2. java中怎么创建栈_这个题如何用栈解呢?

    每日温度 今天又给大家挑了一道十分经典的题目,也是一道面试常考题目,所以大家记得打卡啊,我们先来看一下题目描述,题目很容易理解,而且用暴力法也很容易实现,因为这个题目出现了我们的栈的模块,大家能不能用 ...

  3. Linux内核出错的栈打印详解,linux内核中打印栈回溯信息 - dump_stack()函数分析

    简介 当内核出现比较严重的错误时,例如发生Oops错误或者内核认为系统运行状态异常,内核就会打印出当前进程的栈回溯信息,其中包含当前执行代码的位置以及相邻的指令.产生错误的原因.关键寄存器的值以及函数 ...

  4. 详解+G - 数据结构实验之栈与队列七:出栈序列判定

    理解: 出入栈规律之一,如果前面有一个比较大的数,后面有连续的递增顺序,递增顺序>=2个小于前面比较大的数,那么此出栈顺序不可能实现.比如4,1,2,3,5. 思路:输入一个数,然后不断按照顺序 ...

  5. 【LeetCode之栈和队列】:关于栈和队列经典的OJ题(用C语言实现,附图详解)

    LeetCode题目 1.括号匹配问题 2.用队列实现栈 3.用栈实现队列 4.设计循环队列 1.括号匹配问题 LeetCode链接: [20. 有效的括号] 这道题就是经典的利用栈解决问题的例子:思 ...

  6. js进栈出栈_链栈及基本操作(包含入栈和出栈)详解

    链栈,即用链表实现栈存储结构. 链栈的实现思路同顺序栈类似,顺序栈是将数顺序表(数组)的一端作为栈底,另一端为栈顶:链栈也如此,通常我们将链表的头部作为栈顶,尾部作为栈底,如图 1 所示: 图 1 链 ...

  7. C语言详解括号匹配问题(栈的应用 )

    文章目录 问题概述 算法思路 不匹配的情况 实现流程图 C语言代码 结果测试 问题概述 检测括号是否成对出现 最后出现的左括号最先匹配(LIFO),和栈的后进先出异曲同工 每出现一个右括号,就抵消(出 ...

  8. 1.若元素的进栈序列为:A、B、C、D、E,则:运用栈操作,能否得到出栈序列B、C、A、E、D和D、B、A、C、E?为什么?

    初学数据结构,有一些有意思的东西就写出来记录一下 这个是上课的时候老师出的课堂考试题目,觉得很有意思,分享给大家. 初看题目的时候,第一反应是栈是先进后出,那出栈顺序不就只有一种(E.D.C.B.A) ...

  9. 栈的顺序存储结构(顺序栈)

    栈的顺序存储结构 1.顺序栈的存储结构 #define MAXSIZE 100 //顺序栈存储空间的初始分配 typedef struct{SElemType *base; //栈底指针SElemTy ...

最新文章

  1. WEB攻击手段及防御第2篇-SQL注入
  2. linux下多线程实现服务端
  3. hdu 5617 Jam's maze(双线程dp)
  4. 机器人学习--路径规划算法
  5. 电气期刊论文实现:基于遗传优化的非侵入式居民负荷分解方法(有代码)
  6. aix oracle查看字符集,AIX下oracle 10g 修改字符集为ZHS16GBK
  7. NH3.X与2.X使用上的一些区别
  8. 产品经理思维模型:创新价值曲线
  9. mysql中字典值怎么添加_插入Python字典中的值,包括MySQL的键
  10. php 添加样式,添加样式到php html电子邮件
  11. Nginx For Windows 路由配置
  12. tensorflow打印模型结构_钢结构模型3D打印与有限元网格的融合方法
  13. libev源码分析(一)libev数据结构整理
  14. 微信们正在成为“被模仿者”!中国互联网现状及趋势报告
  15. MediaInfo源代码分析 4:Inform()函数
  16. linux uvc协议_linux 使用 uvc 摄像头
  17. STM32F4图像识别
  18. 洛谷 P3369 【模板】普通平衡树
  19. 自动白平衡(AWB)基础
  20. ghost还原固态硬盘_高级格式化_固态硬盘到底能不能使用Ghost软件?终于说明白了...

热门文章

  1. python图像去雾算法实现
  2. 除了 P 站,程序员居然还喜欢上这些网站?快来摸鱼呀!
  3. 虚拟机出现内部错误解决方法
  4. IIS 配置多域名 多网站时证书错误问题
  5. 斐讯K2 22.4.5.39等带自动重启固件 路由器开启telnet
  6. JavaScript甜点(1)
  7. Shell学习十:su命令
  8. 2021/07/11 老男孩带你21周搞定Go语言 (一)
  9. EXCEL VBA连接不同版本的solidworks
  10. nginx报错400