单调栈详解 + poj 2796 poj 2559 CF 547B

来源:互联网 发布:淘宝网时尚女牛仔裤 编辑:程序博客网 时间:2024/04/29 18:52

单调栈:

定义:用栈结构来实现,使得遍历数组中栈顶元素保持一定范围的最大或最小,并且栈中元素始终保持单调性的栈。

功能:用以快速(O(n))求出数组中某连续子集中的最大值或者最小值。

原理:

以求某连续子集中的最小值为例:

假设某数组为:

下标123456元素316452

模拟过程:

栈顶下标表示当前元素为从此下标到 上一个栈顶下标的最小值。

1.栈为空,将3的下标压入栈中。                                                           //当前栈状态:(栈底 0)1                (栈顶)

2.元素1 <= 3,所以3的下标出栈,1的下标入栈。                             //当前栈状态:(栈底 0)2                (栈顶)

3.元素1 < 6,6的下标入栈。                                                             //当前栈状态:(栈底 0) 2  3           (栈顶) 此时表示6为 2 + 1 到 3 的最小值。

4.元素4 <= 6,6的下标出栈,1 < 4,4的下标入栈。                       //当前栈状态:(栈底0)2 4              (栈顶) 此时表示4为 2 + 1 到 4的最小值。

5.元素4 < 5,5的下标入栈。                                                             //当前栈状态:(栈底0)2 4 5          (栈顶) 此时表示5为 4 + 1 到 5的最小值。

6.元素2 < 5, 5的下标出栈,2<4,4的下标出栈,2的下标入栈。   //当前栈状态:(栈底0)2 6              (栈顶) 此时表示2为 2 + 1 到 6的最小值。

7.更新完毕。


整个过程,每出栈一次记录一次,就可以记录下整个过程中某个连续子段的最小值,达到了其功能。

以下为例题。


poj 2796:

题意:

1e6的数据向量,求连续的子集的和与子集中最小的数的积的最大值, 并求出这段子集的左右坐标。


解析:

不造为啥用stack的stl错了,用数组的过了。

这题有两个注意的点,一个是开始ans置为-1,不然会wa的很惨很惨,因为ans = 0 的时候也更新ansL, ansR。

另一个就是要把-1的节点插入到数组最后,以便单调栈遍历完整个数组。

栈顶为当前的最小值。


代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <climits>#include <cassert>#define LL long long#define lson lo, mi, rt << 1#define rson mi + 1, hi, rt << 1 | 1using namespace std;const int maxn = 1e5 + 10;const int inf = 0x3f3f3f3f;const double eps = 1e-8;const double pi = acos(-1.0);const double ee = exp(1.0);LL a[maxn];LL sum[maxn];int s[maxn];int main(){#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    int n;    while (~scanf("%d", &n))    {        memset(sum, 0, sizeof(sum));        for (int i = 1; i <= n; i++)        {            scanf("%lld", &a[i]);            sum[i] += sum[i - 1] + a[i];        }        a[++n] = -1;///debug        LL ans = -1;///debug        int ansL = -1, ansR = -1;        int top = 0;        for (int i = 1; i <= n; i++)        {            while (top && a[i] <= a[s[top]])            {                LL t = a[s[top]] * (sum[i - 1] - sum[s[top - 1] + 1 - 1]);                if (ans < t)                {                    ans = t;                    ansL = s[top - 1] + 1;                    ansR = i - 1;                }                top--;            }            s[++top] = i;        }        printf("%lld\n%d %d\n", ans, ansL, ansR);    }    return 0;}

poj 2559:

题意:

如上图,给一些宽度为1的矩形的高度,求由矩形围成的大矩形的最大面积。

数据量1e5。

解析:

找每个范围中的最小高度,此时这个最小高度作为整个范围的高,计算面积,扫一遍取最大面积即可。

往前扫一遍,后扫一遍,相当于记录当前这个点a[i]在哪个区域内始终是最小的。

注意的是后扫的时候,要将栈顶置为-1,否则后扫时栈值无法置为n+1。

比如 5 0 0 0 0 1,这组数据。

代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <climits>#include <cassert>#define LL long long#define lson lo, mi, rt << 1#define rson mi + 1, hi, rt << 1 | 1using namespace std;const int maxn = 1e5 + 10;const int inf = 0x3f3f3f3f;const double eps = 1e-8;const double pi = acos(-1.0);const double ee = exp(1.0);LL a[maxn];int s[maxn];//stackint pre[maxn];int las[maxn];int main(){#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    int n;    while (~scanf("%d", &n) && n)    {        memset(s, 0, sizeof(s));        for (int i = 1; i <= n; i++)        {            scanf("%lld", &a[i]);        }        int top = 0;        a[0] = -1;        for (int i = 1; i <= n; i++)        {            while (top && a[i] <= a[s[top]])            {                top--;            }            pre[i] = s[top];            s[++top] = i;        }        top = 0;        s[top++] = n + 1;        a[n + 1] = -1;        for (int i = n + 1; i >= 1; i--)        {            while (top && a[i] <= a[s[top]])            {                top--;            }            las[i] = s[top];            s[++top] = i;        }        LL ans = 0;        for (int i = 1; i <= n; i++)        {//            cout << pre[i] << " " << las[i] << endl;            LL t = (las[i] - pre[i] - 1) * a[i];            if (ans < t)                ans = t;        }        printf("%lld\n", ans);    }    return 0;}

CF 547B:

题目:

Description

Mike is the president of country What-The-Fatherland. There are n bears living in this country besides Mike. All of them are standing in a line and they are numbered from1 ton from left to right.i-th bear is exactlyai feet high.

A group of bears is a non-empty contiguous segment of the line. The size of a group is the number of bears in that group. The strength of a group is the minimum height of the bear in that group.

Mike is a curious to know for each x such that1 ≤ x ≤ n the maximum strength among all groups of sizex.

Input

The first line of input contains integer n (1 ≤ n ≤ 2 × 105), the number of bears.

The second line contains n integers separated by space,a1, a2, ..., an (1 ≤ ai ≤ 109), heights of bears.

Output

Print n integers in one line. For each x from 1 to n, print the maximum strength among all groups of size x.

Sample Input

Input
101 2 3 4 5 4 3 2 1 6
Output
6 4 4 3 3 2 2 1 1 1 

题意:

给n个数,找到长度为1 ~ n 的子集中,每个子集中元素的最小值,然后取代表每个子集的最小值的最大值。

解析:

如上题一样,前后扫一遍,相减出长度。

然后扫一遍就好啦。

感谢这题让我认识了单调栈!


这题有个要注意的地方:

所有长度扫完之后要做一个下面的更新:

for (int i = n - 1; i >= 1; i--){    maxValue[i] = max(maxValue[i + 1], maxValue[i]);}
因为有可能一些长度是取不到的,所以酱紫更新一下就行了。


代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <climits>#include <cassert>#define LL long long#define lson lo, mi, rt << 1#define rson mi + 1, hi, rt << 1 | 1using namespace std;const int maxn = 2e5 + 10;const int inf = 0x3f3f3f3f;const double eps = 1e-8;const double pi = acos(-1.0);const double ee = exp(1.0);int a[maxn];int pre[maxn], las[maxn];int maxValue[maxn];int main(){#ifdef LOCAL    freopen("in.txt", "r", stdin);#endif // LOCAL    int n;    scanf("%d", &n);    for (int i = 1; i <= n; i++)    {        scanf("%d", &a[i]);    }    stack<int> s;    s.push(0);    for (int i = 1; i <= n; i++)    {        while (!s.empty() && a[i] <= a[s.top()])            s.pop();        pre[i] = s.top();        s.push(i);    }    stack<int> t;    t.push(n + 1);    for (int i = n; i >= 1; i--)    {        while (!t.empty() && a[i] <= a[t.top()])        {            t.pop();        }        las[i] = t.top();        t.push(i);    }    for (int i = 1; i <= n; i++)    {//        cout << pre[i] << " " << las[i] << endl;        int len = las[i] - pre[i] - 1;        maxValue[len] = max(maxValue[len], a[i]);    }    for (int i = n - 1; i >= 1; i--)    {        maxValue[i] = max(maxValue[i + 1], maxValue[i]);    }    for (int i = 1; i <= n; i++)    {        printf("%d ", maxValue[i]);    }    return 0;}


0 0
原创粉丝点击