盛水最多的容器(单调栈,双指针)

题目描述

给定$n$个非负整数 $a1,a2,,an$表示平面上有$n$条竖线,第$i$条竖线的两个端点是$(i,ai)$和$(i,0)$

请找出两条竖线,使得它们与$x$轴组成的容器能盛最多的水。

注意:不可以把线倾斜,并且$n2$

输入格式

第一行包含整数$n$。

第二行包含$n$个非负整数。

输出格式

输出一个整数,表示容器的最大盛水量。

数据范围

$1n1e5$,
$1ai1e4$


题解

$l$存储的是从当前位置往左看,最远的一个大于等于当前元素的指(存储该值在原数组中的下标), $r$ 同理.这个$l$数组里面都是一些递增的下标。

比如数组为:1 3 2 4 3 6

那么这个$l$数组中就是1,2,4,6,所以可以二分

这个也可以用双指针

Code

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
int n,a[maxn],l[maxn],r[maxn];
int idx;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        int ll=1,rr=idx,pos=idx;
        while(rr>=ll){
            int mid=(ll+rr)/2;
            if(a[l[mid]]>=a[i]){
                rr=mid-1;
                pos=l[mid];
            } 
            else{
                ll=mid+1;
            }
        } 
        ans=max(ans,min(a[i],a[pos])*(i-pos));
        if(i==1||a[i]>a[l[idx]]) l[++idx]=i; 
    }
    idx=0;
    for(int i=n;i>=1;i--){
        int ll=1,rr=idx,pos=idx;
        while(rr>=ll){
            int mid=(ll+rr)/2;
            if(a[r[mid]]>=a[i]){
                rr=mid-1;
                pos=r[mid];
            }
            else{
                ll=mid+1;
            }
        }
        ans=max(ans,min(a[i],a[pos])*(pos-i));
        if(i==n||a[i]>a[r[idx]]) r[++idx]=i;
    }
    cout<<ans<<endl;
        
}

Code2

#include <iostream>
using namespace std;
const int N = 100010;
int a[N];
int n;
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) scanf("%d", &a[i]);

    int l = 0, r = n - 1;
    int ans = 0;
    while (l < r)
    {
        ans = max(ans, min(a[l], a[r]) * (r - l));
        if (a[l] <= a[r]) ++l;
        else --r;
    }
    printf("%d", ans);
    return 0;
}
双指针

 

posted @ 2021-09-02 19:20  lipu123  阅读(190)  评论(0)    收藏  举报