Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 12926 | Accepted: 3617 | |
Case Time Limit: 1000MS | Special Judge |
Description
Input
Output
Sample Input
6 3 1 6 4 5 2
Sample Output
60 3 5
http://poj.org/problem?id=2796
问题概述:一个数列有n个整数,定义一个区间的val为这个区间的最小值*区间所有数之和,求出val最大的区间(任
一),输出这个最大值以及这个区间的左右端
输入样例: 对应输出:
6 60
3 1 6 4 5 2 3 5
传统方法:枚举每一个a[i],向两边循环以求出以a[i]为最小值的所有的区间对应的val,在其中找出一个最大的,复
杂度n²
单调栈的套路:减少大量的重复计算,复杂度为n(用stack比较慢,用数组模拟栈好像快很多)
步骤:
①定义一个结构体,s[i].x表示第i个元素的值a[i]、s[i].m为第i个元素a[i]的下标i、s[i].l和s[i].r分别表示以a[i]为
最小值的最长区间的两端端点(下标)
②定义一个栈t(存入的是结构体),之后保证这个栈里的元素对应的s[i].x递增
③开个sum[],在输入的时候计算前缀和
④遍历每一个数,假设当前为第i个数a[i],如果a[i]比栈顶元素要大,说明s[i].l = i,s[i]入栈,继续遍历,如果a[i]比
栈顶元素小,执行步骤⑤
⑤弹出当前栈顶元素,这个栈顶元素对应的下标k==t.top().m,那么显然s[k].r = i-1,如果弹出后下一个栈顶元素仍
大于当前元素a[i],继续执行步骤⑤,否则执行步骤⑥
⑥我们假设最后一个弹出的元对应的下标为k,那么可以得出s[i].l = s[k].l,好了我们执行步骤④继续遍历
⑦比一比就输出啦
总结:一旦一个元素已经进入栈,那么这个元素向左扩展的位置(s[i].l)就确定下来了,一旦一个元素被弹出栈,
那么这个元素向右扩展的位置(s[i].r)也确定下来了
#include
#include
using namespace std;
typedef struct
{
int x;
int l, m, r;
}Point;
Point s[100005];
__int64 sum[100005];
stack t;
int main(void)
{
int n, i, l, r;
__int64 ans, temp;
while(scanf("%d", &n)!=EOF)
{
sum[0] = 0;
for(i=1;i<=n;i++)
{
scanf("%d", &s[i].x), s[i].m = i;
sum[i] = sum[i-1]+s[i].x;
}
for(i=1;i<=n;i++)
{
s[i].l = i;
while(t.empty()==0 && t.top().x>s[i].x)
{
s[t.top().m].r = i-1;
s[i].l = s[t.top().m].l;
t.pop();
}
t.push(s[i]);
}
while(t.empty()==0)
{
s[t.top().m].r = n;
t.pop();
}
ans = -1;
for(i=1;i<=n;i++)
{
temp = (sum[s[i].r]-sum[s[i].l-1])*s[i].x;
if(temp>ans)
ans = temp, l = s[i].l, r = s[i].r;
}
printf("%I64d\n%d %d\n", ans, l, r);
}
return 0;
}
/*交G++*/