1. 程式人生 > >poj2796 Feel Good 單調棧

poj2796 Feel Good 單調棧

題目大意:

題意:給你一段區間,需要你求出(在這段區間之類的最小值*這段區間所有元素之和)的最大值......

例如:

6
3 1 6 4 5 2

以4為最小值,向左右延伸,6 4 5  值為60.......

思路:

單調棧的原理:它就是以某一個值為最小(最大)值,向這個值的兩側延伸,遇到大於它(小於它)的值,就將它延伸的範圍擴大,當然,一般來說,要這樣做的演算法複雜度為o(n^2),但是藉助棧,維護其單調增(減),就可以在o(n)的時間複雜度解決這個問題。將一元素加入棧時,

本題用遞增棧

先判斷它是否大於棧頂元素,

  若小於等於棧頂元素,

          棧頂元素出棧, 出棧元素的右端點就確定為當前元素-1

    若是大於棧頂元素,加入棧。棧頂元素+1就是當前節點的左邊界,如果棧為空,則左邊界是起點1,

例如:3 1 6 4 5 2

手工棧s,將s【0】=0;

 把3>0,下標1 加入棧,a[1].l=1;

1<3,  下標1出棧,a[1].r=1;

下標2入棧 , a[2].i=1;

下標3入棧  a[1],<a[3], 3入棧 a[3].l=s.[top]+1=2;

依次進行……

這個題目花了好長時間除錯,,結果早就用了long long ,根據網上ac的題目對拍,因為結果不唯一,改了幾個版本,和別人的對拍都一樣,自己提交就是不過。

後來不知看了哪個人的說明,比較最值的時候要<=才過,改過來,終於過了。

#include<stdio.h>
#include<math.h>
#include<iostream>
using namespace std;
const int maxn=100010;
long long a[100010];
int l[100010],r[100010],s[100010]={0},n,ansl,ansr,top;
long long sum[100010]={0},ans=-1;
int main(){

int i,j;
scanf("%d",&n);

for(i=1;i<=n;i++){
scanf("%I64d",&a[i]);
sum[i]=sum[i-1]+a[i];
l[i]=r[i]=i;
}

a[0]=a[n+1]=-1;
top=0;
s[0]=0;
for(i=1;i<=n+1;i++){

while(top&&a[i]<=a[s[top]]){//a[i]<棧頂,出棧 
 r[s[top]] =i-1;//找出棧頂元素的下標右端點 
 top--;

l[i]=s[top]+1;  
   s[++top]=i;
}

long long tmp;
for(i=1;i<=n;i++){//根據左右端點求值。 
tmp=1LL*(sum[r[i]]-sum[l[i]-1])*a[i];
if(tmp>=ans){
ans=tmp;
ansl=l[i];
ansr=r[i];

}
}
       printf("%lld\n", ans);
       printf("%d %d\n", ansl, ansr);
return 0;
}

大約執行800ms

也可以用stack, 1100多ms

#include<stdio.h>
#include<math.h>
#include<iostream>
#include<stack>
using namespace std;
const int maxn=100010;
long long a[100010];
int l[100010],r[100010],n,ansl,ansr,top;

long long  sum[100010]={0},ans=-1;

int main(){
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
l[i]=r[i]=i;
}
a[0]=a[n+1]=-1;

stack<int>s;   
for(i=1;i<=n+1;i++){
while(!s.empty()&&a[i]<=a[s.top()]  ){//a[i]<棧頂,出棧 
 r[s.top()] =i-1;//找出棧頂元素的下標右端點 
 s.pop();

if(!s.empty())l[i]=s.top()+1;  
else l[i]=1;
   s.push(i);



long long tmp;
for(i=1;i<=n;i++){

tmp=(sum[r[i]]-sum[l[i]-1])*a[i];
if(tmp>=ans){
ans=tmp;
ansl=l[i];
ansr=r[i];
}
}
printf("%lld\n", ans);
  printf("%d %d\n", ansl, ansr);
return 0;
}