链接地址:http://poj.org/problem?id=2796
题目大意:对于给定的一个数列,要求找到一个区间 [l,r] 使得 ans=sum[l,r]∗min[l,r] 尽可能的大。
观察范围可以发现,对于任意一个 ai 我们能找到一个区间 [l,r] ,使得 ai 是这个区间上的最小值。那么现在问题就变成如何对于每一位的 ai 处理出其对应的区间 [l,r] 。
我们可以使用一个栈来维护区间最小的关系。即对于任意的
ai
我们判断栈顶的元素是否大于它,如果大于就
pop
,知道找到第一个比它小的值。这样我们能够维护一个
pre
数组,
pre[i]
代表第一个比
ai
小的元素位置。我们可以用同样的方法方向处理这个数列,得到一个
net
数组,其代表的是在
ai
左边第一个比他小的元素位置。
然后遍历
ai
,维护答案
ans
即可。
#include <cstdio>
#include <string>
#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
#include <map>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<ll, int> pii;
const int MAXN = 100000 + 10;
int pre[MAXN];
int nxt[MAXN];
ll a[MAXN];
ll sum[MAXN];
int main()
{
int n;
while (scanf("%d", &n) != EOF)
{
stack<pii>s;
for (int i = 1; i <= n; i++)scanf("%lld", &a[i]);
memset(sum, 0, sizeof sum);
sum[0] = 0;
for (int i = 1; i <= n; i++)sum[i] = sum[i - 1] + a[i];
for (int i = 1; i <= n; i++)
{
while (!s.empty() && s.top().first >= a[i])s.pop();
if (s.empty())pre[i] = 0;
else pre[i] = s.top().second;
s.push(pii(a[i], i));
}
while (!s.empty())s.pop();
for (int i = n; i >0; i--)
{
while (!s.empty() && s.top().first >= a[i])s.pop();
if (s.empty())nxt[i] = n+1;
else nxt[i] = s.top().second;
s.push(pii(a[i], i));
}
ll ans = 0;
int l=0, r=n+1;
for (int i = 1; i <= n; i++)
{
ll tmp = (sum[nxt[i]-1] - sum[pre[i]])*a[i];
if (tmp > ans)
{
ans = tmp;
r = nxt[i];
l = pre[i];
}
}
printf("%lld\n%d %d\n", ans, l+1,r-1);
}
}