題意:給你一段區間,需要你求出(在這段區間之類的最小值*這段區間所有元素之和)的最大值......
例如:
6 3 1 6 4 5 2
以4為最小值,向左右延伸,6 4 5 值為60.......
思路:解決完為這道題目,我才真正明白了單調棧的原理,它就是以某一個值為最小(最大)值,向這個值的兩側延伸,遇到大於它(小於它)的值,就將它延伸的范圍擴大,當然,一般來說,要這樣做的算法復雜度為o(n^2),但是借助棧這個玩意,維護其單調增(減),就可以在o(n)的時間復雜度解決這個問題。將一元素加入棧時,先判斷它是否大於(小於)棧頂元素,若是大於(小於)棧頂元素,加入棧。(從這里開始只講維護單調增棧)否則,將棧頂元素出棧,直到棧頂元素小於要加入棧的元素,在此過程中,需要維護向前延伸和向后延伸的問題,當要加入棧的元素之前有n個棧元素出棧,那么說明這n個出棧的元素都會大於或者等於要入棧的元素,此時,我們需要維護入棧元素可以向前延伸多少個元素(相當於記錄它的前面有多少個元素比它大),而每個棧頂元素要向出棧了的元素延伸,因為在出棧了的元素一定是比它的大的元素(根據我維護的是單調增棧)......這樣,就在o(n)的時間復雜度內解決了上述問題.........
例如:3 1 6 4 5 2
(3,1,1) (1,2,2) (6,3,3) (4,4,4) (5,5,5) (2,6,6)
首先每個元素自己本身的前后延伸都為1,把3加入棧,1<3,把3出棧,用1的前延伸加上3的前延伸,如此變為(1,1,2),6<1,入棧,變成(1,1,2)(6,3,3),
4<6,將6出棧,4向前延伸,1向后延伸變成(1,1,3) (4,3,4)
5>4,入棧,變成(1,1,3)(4,3,4)(5,5,5)
2<5,5出棧,2向前延伸,4向后延伸,變成(1,1,3)(4,3,5) 2還未入棧(2,5,6)
2<4,4出棧,2向前延伸,1向后延伸,變成(1,1,5) (2,3,6).....
一次類推,會發現最大的結果在(4,3,5)這里這意味着,以4為最小值的區間范圍為3————5,也就是6 4 5
#include<iostream>
#include<stack>
#include<stdio.h>
using namespace std;
#define maxx 110000
__int64 str[maxx],t[maxx];
struct node
{
__int64 num,pre,next; //num記錄數值,pre記錄向前延伸多少個,next記錄向后延伸多少個,k記錄本身所處的位置
__int64 k;
};
int main()
{
int n;
while(scanf("%d",&n)>0)
{
stack<node>Q;
node tmp;
__int64 ans=-100,sum=-100,num;
str[0]=0;
for(__int64 i=1;i<=n;i++)
{
scanf("%I64d",&t[i]);
if(i==1)
str[i]=t[i];
else
str[i]=str[i-1]+t[i];
}
tmp.num=t[1];
tmp.pre=1;
tmp.next=1;
tmp.k=1;
Q.push(tmp);
__int64 x=0,y=0;
for(__int64 i=2;i<=n;i++)
{
node tmp1;
tmp1.num=t[i];
tmp1.pre=tmp1.next=1;
tmp1.k=i;
while(!Q.empty()&&tmp1.num<=Q.top().num)
{
tmp=Q.top();
Q.pop();
if(!Q.empty())
Q.top().next+=tmp.next;
tmp1.pre+=tmp.pre;
ans=tmp.num*(str[tmp.k+tmp.next-1]-str[tmp.k-tmp.pre]);
if(ans>=sum)
{
sum=ans;
x=tmp.k-tmp.pre+1;
y=tmp.k+tmp.next-1;
}
}
Q.push(tmp1);
}
while(!Q.empty())
{
tmp=Q.top();
Q.pop();
if(!Q.empty())
Q.top().next+=tmp.next;
ans=tmp.num*(str[tmp.k+tmp.next-1]-str[tmp.k-tmp.pre]);
if(ans>=sum)
{
sum=ans;
x=tmp.k-tmp.pre+1;
y=tmp.k+tmp.next-1;
}
}
if(n==0)x=y=0;
printf("%I64d\n%I64d %I64d\n",sum,x,y);
}
return 0;
}
