求数组所有区间最大值减去最小值之差的和(贝壳笔试题)

这个题直接暴力求解的话时间复杂度肯定是不行的,所以,我们要计算每个数值的贡献,对每一个数求他当最小值当了多少次,当最大值当了多少次,最后当最大值的次数乘以这个数值减去当最小值的次数乘以数值就得到这个数的贡献,依次把这n个数的贡献加起来就是整个极差之和。

在计算一个数当了多少最值的时候,我们要理解问题,因为区间是连续的,所以,以最小值为例,如果一个数是当前这段区间的最小值,那么他一定是当前这段区间最小的(这不废话),所以,我们就找到他往左做多能找到多少个连续的数都比他大,记录这个位置,同理找他右边有多少个大于它的,这样就得到一个区间,这个区间是以这个数位最小值,如下图示可以比较直观的理解。

加入找以2为最小值的区间,那么他最多可以往左找到3,往右最多可以找到5,那么2作为最小值构成的区间数目为(2+1) * (1+1),如下:

[3, 9, 2], [9, 2], [2], [3, 9, 2, 5], [9, 2, 5], [2, 5]

同理如果2作为最大值也一样求,最大值区间只有[2]这个区间

这个题目还有一个小技巧就是在预处理每个元素作为最值时,最左到什么位置和最右到什么位置,可以利用已知信息,就是前一个求出的位置来跳着加速,使得时间复杂度不是O(n^2)

代码:

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int a[maxn];
int L[maxn], R[maxn];
void print(int L[], int n)
{
    for (int i = 1; i <= n; i++)
        printf("%d ", L[i]);
    puts("");
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    a[0] = -1, a[n + 1] = -1;
    int sum_min = 0;
    //求以当前元素作为最小值时,最左可以扩展到的元素位置.
    for (int i = 1; i <= n; i++)
    {
        if (a[i] >= a[i - 1])
            L[i] = i;
        else
        {
            int tmp = i - 1;
            while (a[i] < a[tmp])
            {
                if (tmp == L[tmp])
                    tmp--;
                else
                    tmp = L[tmp];
            }
            L[i] = tmp + 1;
        }
    }
    //print(L, n);
    //求以当前元素作为最小值时,最右可以扩展到的元素位置.
    for (int i = n; i >= 1; i--)
    {
        if (a[i] >= a[i + 1])
            R[i] = i;
        else
        {
            int tmp = i + 1;
            while (a[i] < a[tmp])
            {
                if (tmp == R[tmp])
                    tmp++;
                else
                    tmp = R[tmp];
            }
            R[i] = tmp - 1;
        }
    }
    //print(R, n);
    //求作为最小值时每个元素的贡献,最后需要减去
    for (int i = 1; i <= n; i++)
    {
        int tmp = (i - L[i] + 1) * (R[i] - i + 1);
        sum_min += tmp * a[i];
    }
    a[0] = inf, a[n + 1] = inf;
    int sum_max = 0;
    //求以当前元素作为最大值时,最左可以扩展到的元素位置.
    for (int i = 1; i <= n; i++)
    {
        if (a[i] <= a[i - 1])
            L[i] = i;
        else
        {
            int tmp = i - 1;
            while (a[i] > a[tmp])
            {
                if (tmp == L[tmp])
                    tmp--;
                else
                    tmp = L[tmp];
            }
            L[i] = tmp + 1;
        }
    }
    //print(L, n);
    //求以当前元素作为最大值时,最右可以扩展到的元素位置.
    for (int i = n; i >= 1; i--)
    {
        if (a[i] <= a[i + 1])
            R[i] = i;
        else
        {
            int tmp = i + 1;
            while (a[i] > a[tmp])
            {
                if (tmp == R[tmp])
                    tmp++;
                else
                    tmp = R[tmp];
            }
            R[i] = tmp - 1;
        }
    }
    //print(R, n);
    //元素作为最大值时的贡献
    for (int i = 1; i <= n; i++)
    {
        int tmp = (i - L[i] + 1) * (R[i] - i + 1);
        sum_max += tmp * a[i];
    }
    printf("%d\n", sum_max - sum_min);
    return 0;
}

原文地址:https://www.cnblogs.com/Howe-Young/p/9499373.html

时间: 2024-10-06 15:52:44

求数组所有区间最大值减去最小值之差的和(贝壳笔试题)的相关文章

写一个方法求数组中的最大值,最小值,总和以及平均值。

class Program { /// <summary> /// 求数组中的最大值,最小值,总和以及平均值. /// </summary> /// <param name="nums">输入一个数组</param> /// <returns>返回一个新的数组(max,min,sum,avg)</returns> public static int[] GetMaxMinSumAvg(int[] nums) { i

分治递归:求数组元素的最大值,最小值

//分治递归,求数组元素的最大值,最小值 /** * 保存产生的最大值,最小值 * @author Administrator * */ public class Values { private int max; private int min; public Values(int max,int min){ this.max=max; this.min=min; } public int getMax() { return max; } public void setMax(int max)

poj 3264 Balanced Lineup(查询区间最大值与最小值的差)

1.代码: #include<stdio.h> #include<string.h> #include<math.h> #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) #define N 100000 int a[N]; int ST1[N][20]; int ST2[N][20]; int n,q; void make_ST() { for(int j=1;(1&l

求数组元素的最大值最小值

这是编程之美上的一个题目: 一般的做法: void main() { int a[5]={78,63,78,67,18}; int min=0,max=0; min=max=a[0]; for(int i=0;i<5;i++) { if(min>a[i]) min=a[i]; if(max<a[i]) max=a[i]; } printf("%d,%d\n",max,min); } 这种方法总共比较了2*N次 如何降低比较次数呢? 我在这里着重记录一下分冶法的做法:

[程序员代码面试指南]栈和队列-最大值减去最小值 小于或等于num 的子数组的数量(双端队列)

题目 给定数组arr和整数num,求数组的子数组中有多少个的满足"最大值减去最小值<=num". 解题思路 分析题目,有结论: 如果数组arr[i...j]满足条件,则它的每一个子数组都满足条件. 如果数组arr[i...j]不满足条件,则包含它的每一个数组都不满足条件. 数据结构:用i.j表示当前窗口,分别使用两个双端队列维护窗口的最大值和最小值. 具体地,每次更新两个双端队列,检查当前的窗口是否满足条件,满足则j++,不满足则cnt+=j-i,更新双端队列,i++,j不变.若

第2章 数字之魅——寻找数组中的最大值和最小值

寻找数组中的最大值和最小值 问题描述 对于一个由N个整数组成的数组,需要比较多少次才能把最大和最小的数找出来呢? 分析与解法 [解法一] 可以把寻找数组中的最大值和最小值看成是两个独立的问题,我们只要分别求出数组的最大值和最小值即可解决问题.最直接的做法是先扫描一遍数组,找出最大的数以及最小的数.这样,我们需要比较2*(N-1)次才能找出最大的数和最小的数.代码如下: 1 package chapter2shuzizhimei.findminmax; 2 /** 3 * 寻找数组中的最大值和最小

最大值减去最小值小于或等于num的子数组数量

[说明]: 本文是左程云老师所著的<程序员面试代码指南>第一章中“最大值减去最小值小于或等于num的子数组数量”这一题目的C++复现. 本文只包含问题描述.C++代码的实现以及简单的思路,不包含解析说明,具体的问题解析请参考原书. 感谢左程云老师的支持. [题目]: 给定数组 arr 和整数 num,共返回多少个字数组满足如下情况: max(arr[i...j]) - min(arr[i...j]) <= num max(arr[i...j]) 表示字数组 arr[i...j] 中的最大

编程之美3:寻找数组中的最大值和最小值以及最大值和次大值

很开心,这是今天的第三篇文章啦!下午健身也感觉非常过瘾,托付宿舍妹子从日本代购的护肤品也到了.耳边漂浮着Hebe田馥甄的<魔鬼中的天使>文艺的声线,一切都好棒,O(∩_∩)O哈哈~.爱生活,爱音乐,爱运动,额,当然还有要爱学习啦!加油(^ω^) 额,扯远了.第三篇是关于寻找数组中的最大值和最小值.第一次看到这个题目的时候,楼主稍微鄙视了一下,因为觉得这个题目有什么好做的.但是楼主还是看了看<编程之美>上的写的,发现还是有必要记录一下,不一样的思考方式.很赞!大家和楼主一起哦,Are

2.10 用最少次数寻找数组中的最大值和最小值[find min max of array]

[本文链接] http://www.cnblogs.com/hellogiser/p/find-min-max-of-array.html [题目] 对于一个由N个整数组成的数组,需要比较多少次才能把最大和最小的数找出来呢? [分析] 1. 遍历两次数组,分别找出最大值和最小值,需要进行 2N 次比较. 2. 将数组中的元素分组,按顺序将数组中相邻的两个数分在同一组,用Max和Min来存储最大值和最小值.同一组比较完之后,较小的数与当前的最小值比较,如该数小于当前最小值,更新Min:较大的数与当