Loading

[POJ - 2796]Feel Good

题目传送门problem_link

题意:给出一个区间,选择这段区间的某个子区间,使得在这段子区间内的元素最小值*这段区间所有元素之和最大。

具体看代码吧。

#include <cstdio>
#include <cstring>
#include <stack>
#include <iostream>
using namespace std;
#define N 100000 + 100
int a[N], lef[N], sta[N], top;
long long sum[N];
stack<int> s;
int main()
{
    long long ans = -1, tmp;
    int n;
    int ll, rr;

    while (~scanf("%d", &n))
    {
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", a + i);
            sum[i] = sum[i - 1] + a[i]; //前缀和预处理
        }
        a[++n] = -1;
        top = 0;
        for (int i = 1; i <= n; i++)
        {
            //维护的是一个单调增的栈(维护最小元素)
            if (s.empty() || a[i] > a[s.top()])
            {
                s.push(i);
                lef[i] = i; //记录第i个元素以自身为最小元素的左边界
                continue;
            }
            if (a[i] == a[s.top()])
                continue;

            int cur;
            //若出现比栈顶小的元素就一直出栈到大于栈顶元素为止
            while (!s.empty() && a[i] < a[s.top()])
            {
                cur = s.top();s.pop();
                //因为出栈,故记录出栈元素所在的区间和*自身(因为自身是这段区间的最小元素)
                tmp = a[cur] * (sum[i - 1] - sum[lef[cur] - 1]);
                if (tmp > ans)
                {
                    //更新最大值,记录最左和最右下标
                    ll = lef[cur];
                    rr = i - 1;
                    ans = tmp;
                }
            }
            //个人认为这里是单调栈最难理解的地方
            //将i重新入栈,并且将其以自身为最小元素的左边界更新到最后一个出栈元素的边界
            //要是不懂可以在纸上推一下,真的是很奇妙的东西
            //这可能就是所谓的算法艺术吧~
            lef[i] = lef[cur];
            s.push(i);
        }

        printf("%lld\n%d %d\n", ans, ll, rr);
    }
    return 0;

}

 

posted @ 2019-08-20 13:16  ViKyanite  阅读(118)  评论(0编辑  收藏  举报