POJ 2796 单调栈
题意:给出n个数,定义区间的值=这段区间之内的最小值 * 这段区间所有元素之和,求出这n个数可能的区间最大值。
tags: 好题, 可参考大神博客
主要思路:对于每个数,找出以它为最小值的最大区间。 这个怎么找呢?暴力来O(N^2)肯定超时,可以借助单调栈,每个数定出左右延伸的范围。
比如样例: 6 3 1 6 4 5 2 。 维护单调递增栈,开始时(6,1,1)入栈,然后(3,2,2)因为3<6,所以(6,1,1)出栈,同时表明3可以掌管到6,故(3,2,2)向左延伸变为(3,1,2)并入栈。 依次进行这样的操作就可以O(n)求出以每个数为最小值的最大区间,然后在这些区间中找最大的即是答案。
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<bitset> #include<vector> #include<set> #include<list> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a;i<=b;i++) #define per(i,b,a) for (int i=b;i>=a;i--) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f typedef long long ll; const int N = 200005; struct Point {int ai, l, r; }p[N]; Point sta[N*2], ans; int n, top; ll sum[N]; int main() { scanf("%d", &n); rep(i,1,n) { scanf("%d", &p[i].ai); p[i].l=p[i].r=i; sum[i]=sum[i-1]+p[i].ai; } sta[++top]=p[1]; ans=p[1]; rep(i,2,n) { if(top>0 && p[i].ai<=sta[top].ai) { while(top>0 && p[i].ai<=sta[top].ai) { p[i].l=sta[top].l; if(top-1>0) sta[top-1].r=sta[top].r; if((sum[ans.r]-sum[ans.l-1])*ans.ai <= (sum[sta[top].r]-sum[sta[top].l-1])*sta[top].ai) ans=sta[top]; --top; } sta[++top]=p[i]; } else { sta[++top]=p[i]; } } while(top>0) { if((sum[ans.r]-sum[ans.l-1])*ans.ai <= (sum[sta[top].r]-sum[sta[top].l-1])*sta[top].ai) ans=sta[top]; if(top-1>0) sta[top-1].r=sta[top].r; --top; } printf("%lld\n%d %d\n", (sum[ans.r]-sum[ans.l-1])*ans.ai, ans.l, ans.r); return 0; }