单调栈

单调栈只是一种优化工具,而非一种新的解题思路,一般使用单调栈的题目使用暴力都可以得到正确结果只是会超时,然后我们通过分析发现暴力做法中的某些数据能够保证一定不是答案,这些数据一般会满足”对当前没用,那么对后面一定没用”的性质,所以我们就可以去掉这些元素从而减低程序的复杂度。目前接触到的类型只有 距离最近的较小值距离最近的较大值 这两种。稍微复杂一点的就是解题过程中的某一部分用到,需要自己抽象出这个模型才可以。

距离最近的较小值

copy.png

/**
 * 暴力
 */
#include <iostream>
 
using namespace std;
 
const int N = 100010;
 
int n, a[N];
 
int main()
{
    cin >> n;
    for (int i = 0; i < n; ++ i)
    {
        cin >> a[i];
        bool find = false;
        for (int j = i - 1; j >= 0; -- j)
            if (a[j] < a[i]) 
            {
                cout << a[j] << ' ';
                find = true;
                break;
            }
        if (!find) cout << -1 << ' ';
    }
}
 
/**
 * 正解
 * 暴力的问题是不管三七二十一j一定会全遍历一遍
 * 对于这种情况 1 10 10 10 10 10 10 ...后面全是10
 * 前面的10对于后面的10明显都是非法答案,但j还是要扫一遍,肯定会超时
 * 这种情况可以抽象为对于i < j,a[i] >= a[j], 这样的a[i]显然不可能是a[j + 1], a[j + 2]... 的答案,因为更靠近它们的有a[j]
 * 所以我们完全可以不用考虑这样的a[i],也就是把a[j]之前比它大的数全都去掉对后面的结果是不产生影响的
 * 下面这种做法之所以叫做单调栈是因为栈中保存的始终是一个单调的序列,因为过程中把所有的逆序对全都删除了
 */
#include <iostream>
 
using namespace std;
 
const int N = 100010;
 
int n, a[N];
int stk[N], tt;
 
int main()
{
    cin >> n;
    for (int i = 0; i < n; ++ i) 
    {
        cin >> a[i];
        while (tt > 0 && stk[tt] >= a[i]) -- tt;
        if (tt > 0) cout << stk[tt] << ' ';
        else cout << -1 << ' ';
        stk[++ tt] = a[i];
    }
    return 0;
}
原文地址:https://www.cnblogs.com/G-H-Y/p/14299710.html