单调栈学习笔记
第一题
题目描述
约翰有N头奶牛,编号为1到N。
现在这N头奶牛按编号从小到大的顺序站成了一排,其中奶牛 i 的身高为Hi。
现在,每头奶牛都向它的右侧望向那些编号较大的奶牛,对于奶牛 i 如果存在一头奶牛 j 满足 \(i<j\) 并且 \(H_i<H_j\),那么我们称奶牛 i 需要仰视奶牛 j。
请你求出每头奶牛的最近仰视对象。
输入格式
第一行包含整数N。
接下来N行,每行包含一个整数\(H_i\),其中第 i 行的数为编号为 i 的奶牛的高度。
输出格式
共 N 行,每行输出一个整数,其中第 i 行的输出整数表示编号为 i 的奶牛的最近仰视对象的编号,如果不存在仰视对象,则输出0。
数据范围
\(1 \le N \le 10^5\)
\(1 \le H_i \le 10^6\)
输入样例:
6
3
2
6
1
1
2
输出样例:
3
3
0
6
6
0
解题思路
题意分析
这道题目大致意思是:每一头奶牛往右看,找到离自己最近,而且比自己身高高的牛,
如果没有比自己高的牛,那么输出0即可.也就是无解判断
算法分析
首先我们知道程序=数据结构+算法,这道题目算法,我们除了暴力+模拟,实在想不到任何解题思路.可能是我太蠢了
所以我们考虑如何通过数据结构来优化这道题目.
数据结构
对于一道题目而言,我们需要对于条件,性质两处地方动手,来思考算法或者数据结构的突破口,显然这道题目数据结构的确定,同样离不开这条不定的定律.
对于这道题目而言,我们主要是分析条件,因为我们发现这道题目所有的奶牛,都在找离着自己最近的奶牛,那么我们不得不思考,是不是要用到后进先出的栈
既然现在我们已经确定,数据结构大致为栈,那么现在我们就需要分析性质了.
分析性质
这道题目,最有用的性质,就是离自己最近,而且比自己身高高.
- 离自己最近:这个性质其实就是我们所谓的栈的必备性质.
- 身高高:看到这种类型的词汇,一定要第一时间反应,这道题目是不是拥有单调性.
经过上面的讨论,我们大致可以确定,这道题目的确拥有单调性,那么想让我们的数据结构栈,就进化成为了单调栈.
算法整合
我们可以一步步读入奶牛,对于每一头奶牛而言,判断这一头奶牛可以成为哪些奶牛的仰视对象.
于是,我们可以将当前奶牛,不断地和栈顶奶牛比较,如果说它身高大于栈顶奶牛,那么栈顶奶牛的仰视对象一定是当前奶牛,然后将栈顶奶牛出栈,进行下一次比较,直到栈为空或者栈顶奶牛身高高于它.最后再将我们当前奶牛的身高入栈.
之所以仰视对象是当前奶牛,因为它是离栈顶奶牛最近的奶牛,而且满足身高大于它.
可以略微证明一下,因为如果说栈顶奶牛的仰视对象不是当前这头奶牛,那么在这头奶牛之前,栈顶奶牛肯定已经出栈了,因为必然在此之前,会有奶牛成为栈顶奶牛的仰视对象,然而现在它还在栈中,那么栈顶奶牛的仰视对象,必然是当前这头奶牛.
代码
#include <bits/stdc++.h>
using namespace std;
const int N=101000;
int n,m,i,j,k,a[N],s[N];
stack<pair<int,int> > q;
int main()
{
ios::sync_with_stdio(false);//优化不可少
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
while (q.size() && a[i]>q.top().first)//栈内有奶牛,且身高大于栈顶的奶牛
{
s[q.top().second]=i;//仰视对象
q.pop();
}
q.push(make_pair(a[i],i));//加入栈中
}
for(int i=1;i<=n;i++)
cout<<s[i]<<endl;//输出即可
return 0;
}
第二题
题目描述
某地有 N 个能量发射站排成一行,每个发射站 i 都有不相同的高度 \(H_i\),并能向两边(当然两端的只能向一边)同时发射能量值为 \(V_i\) 的能量,并且发出的能量只被两边最近的且比它高的发射站接收。
显然,每个发射站发来的能量有可能被 0 或 1 或 2 个其他发射站所接受,出于安全考虑,每个发射站接收到的能量总和是我们很关心的问题。
由于数据很多,现在只需要你帮忙计算出接收最多能量的发射站接收的能量是多少。
输入格式
第一行包含整数N。
接下来N行,每行包含两个整数\(H_i\)和\(V_i\),其中第 i 行的数据为第 i 个发射站的高度和能量值。
输出格式
输出仅一行,表示接收最多能量的发射站接收到的能量值。
数据保证答案不超过\(2^{31}-1\)。
数据范围
\(1 \le N \le 10^6\),
\(1 \le H_i \le 2*10^9\),
\(1 \le V_i \le 10000\)
输入样例:
3
4 2
3 5
6 10
输出样例:
7
解题思路
题意分析
N 个能量发射站排成一行,每个发射站 i 都有不相同的高度 \(H_i\),并能向两边同时发射能量值为 \(V_i\) 的能量,并且发出的能量只被两边最近的且比它高的发射站接收。然后要我们求出这个最大的接受能量值是多少.
思路分析
首先我们知道程序=数据结构+算法,这道题目算法,我们除了暴力+模拟,实在想不到任何解题思路.可能是我太蠢了
所以我们考虑如何通过数据结构来优化这道题目.
数据结构
既然现在我们已经想到了数据结构来优化算法,那么现在当前最大的问题.无非就是如何利用这个我们学过数据结构来优化这道题目.
我们发现这道题目,所有的数字都满足一个非常重要的性质,那就是发出的能量只被两边最近的且比它高的发射站,我们从中间,不但会发现这道题目的条件,还会发现这道题目出题人,偷偷告诉我们的性质.那就是两边最近且比他高.
性质分析
两边最近: 显然,是一个条件&性质,而且这里面最为重要的性质核心,就是最近这个两个字.
看到这里,我们就得让神经系统中的神经元,条件反射地想到,是不是需要后进后出的数据结构栈
比他高: 这就是这道题目的第二大精髓思想,单调性,我们通过这道题目的这句话,可以敏锐地察觉到,这道题目需要使用具有单调性质的单调栈.
算法步骤
这道题目既然是需要使用具有非常秀的单调栈,那么我们到底如何使用呢?
那么此时我们就需要根据条件,来确定单调栈,插入栈顶的条件.
因为对于一个发射站而言,它可以接收到的能量,就是一组单调递减的高度序列的能量.这里我们需要画图解决问题.
综上所述,我们可以开一个单调递减的栈,统计所有高度单调递减的发射站.
- 如果当前这个数字破坏了单调递减,那么它会挡掉比它矮的所有发射站.
然后将所有比它能量小的发射站,统统吸取.然后将这些发射站出栈,自己入栈. - 如果当前发射站,满足单调递减的话,那么栈顶所属的发射站,吸取它的能量.同样自己也需要入栈
代码实现
#include <bits/stdc++.h>
using namespace std;
const int N=1001000;
pair<int,int> p[N];
int top,x,n,m,i,j,s[N],a[N],b[N],ans,top2;
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i]>>b[i];//读入
p[++top].first=a[1];//first存储高度
p[top].second=1;//存储这个发射塔的位置
for(int i=2; i<=n; i++)
{
while (a[i]>p[top].first && top)
s[i]+=b[p[top--].second];//将这个发射塔的能量吸取
s[p[top].second]+=b[i];//栈顶吸收我这个发射塔的能量
p[++top]=make_pair(a[i],i);//插入栈中
}
for(int i=1; i<=n; i++)
ans=max(ans,s[i]);
cout<<ans;
return 0;
}