单调栈学习笔记

第一题

题目描述

约翰有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即可.也就是无解判断

算法分析

首先我们知道程序=数据结构+算法,这道题目算法,我们除了暴力+模拟,实在想不到任何解题思路.可能是我太蠢了

所以我们考虑如何通过数据结构来优化这道题目.

数据结构

对于一道题目而言,我们需要对于条件,性质两处地方动手,来思考算法或者数据结构的突破口,显然这道题目数据结构的确定,同样离不开这条不定的定律.

对于这道题目而言,我们主要是分析条件,因为我们发现这道题目所有的奶牛,都在找离着自己最近的奶牛,那么我们不得不思考,是不是要用到后进先出的栈

既然现在我们已经确定,数据结构大致为栈,那么现在我们就需要分析性质了.

分析性质

这道题目,最有用的性质,就是离自己最近,而且比自己身高高.

  1. 离自己最近:这个性质其实就是我们所谓的栈的必备性质.
  2. 身高高:看到这种类型的词汇,一定要第一时间反应,这道题目是不是拥有单调性.

经过上面的讨论,我们大致可以确定,这道题目的确拥有单调性,那么想让我们的数据结构栈,就进化成为了单调栈.

算法整合

我们可以一步步读入奶牛,对于每一头奶牛而言,判断这一头奶牛可以成为哪些奶牛的仰视对象.
于是,我们可以将当前奶牛,不断地和栈顶奶牛比较,如果说它身高大于栈顶奶牛,那么栈顶奶牛的仰视对象一定是当前奶牛,然后将栈顶奶牛出栈,进行下一次比较,直到栈为空或者栈顶奶牛身高高于它.最后再将我们当前奶牛的身高入栈.

之所以仰视对象是当前奶牛,因为它是离栈顶奶牛最近的奶牛,而且满足身高大于它.

可以略微证明一下,因为如果说栈顶奶牛的仰视对象不是当前这头奶牛,那么在这头奶牛之前,栈顶奶牛肯定已经出栈了,因为必然在此之前,会有奶牛成为栈顶奶牛的仰视对象,然而现在它还在栈中,那么栈顶奶牛的仰视对象,必然是当前这头奶牛.

代码
#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) 的能量,并且发出的能量只被两边最近的且比它高的发射站接收。然后要我们求出这个最大的接受能量值是多少.

思路分析

首先我们知道程序=数据结构+算法,这道题目算法,我们除了暴力+模拟,实在想不到任何解题思路.可能是我太蠢了

所以我们考虑如何通过数据结构来优化这道题目.

数据结构

既然现在我们已经想到了数据结构来优化算法,那么现在当前最大的问题.无非就是如何利用这个我们学过数据结构来优化这道题目.

我们发现这道题目,所有的数字都满足一个非常重要的性质,那就是发出的能量只被两边最近的且比它高的发射站,我们从中间,不但会发现这道题目的条件,还会发现这道题目出题人,偷偷告诉我们的性质.那就是两边最近且比他高.

性质分析

两边最近: 显然,是一个条件&性质,而且这里面最为重要的性质核心,就是最近这个两个字.

看到这里,我们就得让神经系统中的神经元,条件反射地想到,是不是需要后进后出的数据结构栈

比他高: 这就是这道题目的第二大精髓思想,单调性,我们通过这道题目的这句话,可以敏锐地察觉到,这道题目需要使用具有单调性质的单调栈.

算法步骤

这道题目既然是需要使用具有非常秀的单调栈,那么我们到底如何使用呢?

那么此时我们就需要根据条件,来确定单调栈,插入栈顶的条件.

因为对于一个发射站而言,它可以接收到的能量,就是一组单调递减的高度序列的能量.这里我们需要画图解决问题.

综上所述,我们可以开一个单调递减的栈,统计所有高度单调递减的发射站.

  1. 如果当前这个数字破坏了单调递减,那么它会挡掉比它矮的所有发射站.
    然后将所有比它能量小的发射站,统统吸取.然后将这些发射站出栈,自己入栈.
  2. 如果当前发射站,满足单调递减的话,那么栈顶所属的发射站,吸取它的能量.同样自己也需要入栈
代码实现
#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;
}
原文地址:https://www.cnblogs.com/gzh-red/p/11019265.html