题目描述
给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器,请返回容器能装多少水。
输入描述:
第一行一个整数N,表示数组长度。
接下来一行N个数表示数组内的数。
输出描述:
输出一个整数表示能装多少水。

输入
6
3 1 2 5 2 4
输出
复制
5

思路:
对于L位置,其水平线为min{左数最大值,右侧最大值}

方法1
初始想法:先找到最高点,对于左右两边,分别依次找次高点,直到次高点为边界,每次加上次高点中间的水量(简单的说,就是从最高点向两边找“坑”)

#include<iostream>
#include<vector>
using namespace std;
#define LENGTH 1000001

int arr[LENGTH];
int getMaxID(int l, int r)
{
    int max = arr[l];
    int maxID = l;
    for (int i = l+1; i < r; ++i)
    {
        if (max < arr[i])
        {
            max = arr[i];
            maxID = i;
        }
    }
    return maxID;
}
long long getLSum(int l, int r)
{
    long long sum = 0;
    for (int i = l+1; i < r; ++i)
    {
        sum += arr[l] - arr[i];
    }
    return sum;
}
long long getRSum(int l, int r)
{
    long long sum = 0;
    for (int i = l; i < r; ++i)
    {
        sum += arr[r] - arr[i];
    }
    return sum;
}

int main()
{
    int N;
    cin >> N;
    long long sum = 0;
    for (int i = 0; i < N; ++i)
    {
        cin >> arr[i];
    }
    int maxID = getMaxID(0, N);        
    int p = maxID;//左标
    int q = maxID;//右表
    while (p != 0)
    {
        int p1 = getMaxID(0, p);
        sum += getLSum(p1, p);
        p = p1;
    }
    while (q != N-1)
    {
        int q1 = getMaxID(q+1, N);
        sum += getRSum(q+1, q1);
        q = q1;
    }
    cout << sum;
}

想法比较朴素,超时,通过90%
方法2
参考网络上的想法:由于对每个点来说,水平线都为左数组最大值和右数组最大值中较小的那个值,可以通过辅助数组记录每个点左右数组最高值,时间复杂度为O(N)。

int main()
{
    int N;
    cin >> N;
    long long sum = 0;
    vector<int> arr(N), l(N, 0), r(N, 0);
    for (int i = 0; i < N; ++i)
    {
        cin >> arr[i];
    }
    for (int i = 0; i<N - 1; ++i)
    {
        l[i + 1] = arr[i]>l[i] ? arr[i] : l[i];
        r[N - i - 2] = arr[N - i - 1] > r[N - i - 1] ? arr[N - i - 1] : r[N - i - 1];
    }
    for (int i = 0; i < N; ++i)
    {
        if (arr[i]>l[i] || arr[i] > r[i]) continue;
        else if (l[i] > r[i]) sum += r[i]-arr[i];
        else sum += l[i]-arr[i];
    }
    cout << sum;
}

很好理解,并且顺利通过
方法三:
方法二中,两个辅助数组都是递推的,可以边遍历,边生成两个辅助值。
设置两个指针L和R,初始分别指向arr[1]和arr[arr.length-2]位置,指定两个变量leftMax和rightMax,初始分别为arr[0]和arr[arr.length - 1],leftMax表示[0…L-1]位置中的最大值,rightMax代表[R+1…arr.length-1]位置上的最大值。
根据leftMax与rightMax大小进行指针移动,进行遍历,可减小空间复杂度。
方法四:
XWFLS大佬的方法。直接建立水平线,每次更新,精妙极了,如下:

int main()
{
    int N;
    cin >> N;
    long long sum = 0;
    vector<int> arr(N);
    for (int i = 0; i < N; ++i)
    {
        cin >> arr[i];
    }
    auto l = arr.begin();//左指针
    auto r = --arr.end();//右指针
    int lower = 0;
    int level = 0;
    while (l < r)
    {
        lower = *l < *r ? *l++ : *r--;
        level = level>lower ? level : lower;
        sum += level - lower;
    }
    cout << sum << endl;
}

核心代码就三行,太精致了!!!