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;
}
posted @ 2021-09-09 17:02  糖豆爸爸  阅读(132)  评论(0)    收藏  举报
Live2D