题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=2500
对准备看这个的同学们说一句忠告:
1、如果你们想看这道题的枚举办法,再见~。
我们这里没有讲这个
2、如果你连栈是啥都不知道,再见~
我们这里也没有讲这个
3、如果你知道单调栈,不知道关系的话,欢迎加入群聊~
你可以看,讲了
4、你知道栈,不知道单调栈,欢迎加入群聊~
你可以看,讲了
5、如果你上面4种方式都不是,看完请评论
请帮忙评论,说出你是什么情况
一、题目描述
小b有一个长度为n的序列t,现在她对于每个i,求最小的正数j满足 i + j ≤ n 且 t[i+j] > t[i],输出 j,如果不存在这样的 j,则输出0。
样例解释:
对于 i = 1,t[2] > t[i],所以最小的 j = 1
对于 i = 7,不存在这样的j,所以输出0
题意简化:给定序列,问序列中每个数后面第一个比它大的在哪,距离t[i]多远。
不要看上面那一大段,都没用
二、解题思路
这道题看上去只用枚举大法就可以了,实际上这道题是单调栈。
现在来科普一下什么是单调栈。要相信一开始弄了3天没弄懂的我,应该(注意是应该,不是肯定)不会让大家失望
单调栈
栈结构是一种先进后出的数据结构,单调栈是在栈的基础上实现的一种数据结构。单调栈就是栈内元素
单调递增或者单调递减的栈。
若是单调递增栈,则从栈顶到栈底的元素是严格递增的。若是单调递减栈,则从栈顶到栈底的元素是严
格递减的。
重要的部分来了~
我相信你们看上面已经看傻了,(没办法啊,这是书上的定义,得给大家看官方定义嘛)
总之,下面很重要
元素进栈过程:对于单调递增栈,若当前进栈元素为 e,从栈顶开始遍历元素,把小于e或者等于e的
元素弹出栈,直接遇到一个大于e的元素或者栈为空为止,然后再把e压入栈中。对于单调递减栈,则
每次弹出的是大于e或者等于e的元素。
明白了单调栈的加入元素的过程后,我们来看看它的性质,以及为啥要用单调栈。单调栈的一大优势就
是时间复杂度为O(n)的。
以3、4、2、6、4、5、2、3为例:
第几步 | 操作 | 站内元素 |
1 | 3进栈 | 3 |
2 | 3出栈,4进栈 | 4 |
3 | 2进栈 | 4,2 |
4 | 2、4出栈,6进栈 | 6 |
5 | 4进栈 | 6,4 |
6 | 4出栈,5进栈 | 6,5 |
7 | 2进栈 | 6,5,2 |
8 | 2出栈 | 6,5,3 |
现在我们明白什么是单调栈了,这道题跟这个有什么关系呢?
关系来了:
因为要找的是每个数后面第一个比它大的,所以考虑倒着做。维护一个单调减的单调栈(越靠栈顶值越
小),插入当前这个数的时候不断从栈顶弹出那些小于等于它的,直到遇见一个比它大的,那么这个数
就是“它后面第一个比它大的了”。
我们不妨考虑一下,这样操作为什么是合理的。如果一个数既小又靠后,那么这个数是没有什么意义
的,因此被踢掉不会影响正确性。
到这里大家应该都明白了吧,献上代码
三、代码答案
思路一:枚举大法(不推荐,毕竟人家都讲了这么久了最后你们还是用暴力。选择这个思路会使我心情失落的!所以大家不要选)
但还是线上代码吧,作为参考,但我就懒得加注释了
#include<cstdio> #include<iostream> using namespace std; int main(){ int n, t[30010]; bool flag = 0; cin >> n; for(int i = 1;i <= n;i++){ cin >> t[i]; } for(int i = 1;i <= n;i++){ for(int j = 1;i+j <= n;j++){ if(t[i+j] > t[i]){ cout << j << ' '; flag = 1; break; } if(i+j == n){ cout << 0 << ' '; flag = 1; break; } } flag = 0; if(flag == 0 && i == n){ cout << 0 << ' '; } } return 0; }
思路二(特别推荐使用,也不是“特别”吧,但是比起暴力更推荐)
这个给大家加上注释吧,毕竟肯定会有看上面看烦了直接看下面的同学,但是还是建议先看思路可能要不然看不懂
#include<stack> #include<cstdio> #include<iostream> using namespace std; long long n, a[30010], ans[30010]; stack <int> s; /* 9 5 2 3 1 7 6 4 5 6 8 73 74 75 71 69 72 76 73 */ int main(){ cin >> n; for(int i = 1;i <= n;i++){ cin >> a[i]; } for(int i = n;i >= 1;i--){//大家注意这里是从n开始的 //如果s里面还有数 //并且我后面的数(上一个存在s里的)比现在这个大 //说明现在这个的答案就是我已经存在s里的了 //而且还说明现在这个a[i]没有用,可以踢出去 while(!s.empty() && a[s.top()] <= a[i]){//如果 s.pop();//踢出a[i] } if(s.empty()){//如果s里面没有数字了 ans[i] = 0;//这个数没有答案,没有数比他大 //所有数都被刚才淘汰掉了,或者这是a[n] }else{//如果s里面还是有数字的 ans[i] = s.top() - i;//往答案里面存数字 //因为题目要求我们求的是距离,所以我在s里面存的也是下标 //只要拿上一个存在s里的数的下标-这次的下标就可以了 } s.push(i);//s里面存的是下标哦集美们 } for(int i = 1;i <= n;i++){ cout << ans[i] << ' ';//输出答案 } return 0; }