600.奶牛的仰望

AcWing题目传送门
洛谷题目传送门

一、题目描述

约翰有(N)头奶牛,编号为(1)(N)

现在这(N)头奶牛按编号从小到大的顺序站成了一排,其中奶牛 (i) 的身高为(H_i)

现在,每头奶牛都向它的右侧望向那些编号较大的奶牛,对于奶牛 (i) 如果存在一头奶牛 (j) 满足 (i<j) 并且 (H_i<H_j),那么我们称奶牛 (i) 需要仰视奶牛 (j)

请你求出每头奶牛的最近仰视对象。

输入格式
第一行包含整数(N)

接下来(N)行,每行包含一个整数(H_i),其中第 (i) 行的数为编号为 (i) 的奶牛的高度。

输出格式
(N) 行,每行输出一个整数,其中第 (i) 行的输出整数表示编号为 (i) 的奶牛的最近仰视对象的编号,如果不存在仰视对象,则输出(0)

数据范围
(1≤N≤10^5)
(1≤H_i≤10^6)

输入样例:

6 
3  2  6  1  1  2 

输出样例

3 3 0 6 6 0 

二、解题思路

结论:找出离自己最近比自己大(或小)的,用单调栈。

最朴素的办法就是暴力,遍历每一个奶牛,然后向它右边遍历所有其它奶牛,找出离自己最近并且高于自己的奶牛。

这么做性能并不是很高,比如 ([1,2,2,2,2,2,2,3,3,3,3,3,3]),这样的测试数据,第一个(2)的那个,需要向右找(6)个数字才能找到比自己大的;而第二个(2),则需要向右找(5)个数字才能找到比自己大的。

想要性能好,该怎么办呢?我们需要知道为什么朴素作法慢,它到底哪里可提速呢?

很明显,上面的办法中有大量的重复运算,比如明明知道第(2)个位置和第(3)个位置一样,结果,还是傻傻的每次去重新开始找,能不慢吗?

单调栈的思路是这样的:
1)设计一个栈,让每个奶牛都带着“我需仰望哪位?”的问题进入栈,一旦有牛能回答这个问题,它就出栈。

2)每个奶牛进栈之前,都与与它最近的已入栈奶牛对比身高,如果它比离它近的高,那么它就是栈顶奶牛的答案:“你需仰望大爷我!”,栈顶的知道答案后,就退出了栈。

3)反复执行上面的过程,把栈顶的奶牛都处理掉,直到栈为空或者栈顶奶牛的高度大于要入栈的奶牛,再把当前的奶牛入栈,因为它自己也想知道答案。

其实单调栈就是解决了两个问题“比你高”,“离你近”。

比你高是靠与栈顶元素对比来的,离你近是因为栈本身的性质。

三、数组模拟栈法

#include <bits/stdc++.h>

//奶牛的仰望
//https://www.cnblogs.com/littlehb/p/15247842.html
using namespace std;
const int N = 101000;

int n;    //多少只奶牛
int a[N]; //每只奶牛的身高
int s[N]; //谁仰望谁

//用数组模拟栈,每一个栈内的元素,都是需要找到“比自己高度大的,离自己最近”的奶牛。
int stk[N]; //内容:序号
int tt;     //指针,默认是0


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 (tt && a[i] > a[stk[tt]]) {
            s[stk[tt]] = i;//仰视对象
            tt--;//出栈
        }
        stk[++tt] = i;//加入栈中,等待找到需它仰视的奶牛。
    }
    for (int i = 1; i <= n; i++) cout << s[i] << endl;//输出即可
    return 0;
}

四、STL栈法

#include <bits/stdc++.h>

//奶牛的仰望
//https://www.cnblogs.com/littlehb/p/15247842.html
using namespace std;
const int N = 101000;

int n;    //多少只奶牛
int a[N]; //每只奶牛的身高
int s[N]; //谁仰望谁
stack<int> stk;

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 (stk.size() && a[i] > a[stk.top()]) {
            s[stk.top()] = i;//仰视对象
            stk.pop();//找到了仰望对象,就不再需要继续找了
        }
        stk.push(i);//加入栈中,等待找到需它仰视的奶牛。
    }
    for (int i = 1; i <= n; i++) cout << s[i] << endl;//输出即可
    return 0;
}
原文地址:https://www.cnblogs.com/littlehb/p/15247842.html