美团笔试算法题第二题(3.22)

题目描述:给定一个 n 个数,求解有多少个长度为奇数的区间的中位数位置刚好就在正中间。

输入:

第一行输入 t ,表示 t 组输入

第二行输入 n,数组大小为 n

第三行输入 n 个数。

t 组输入的 n 的个数和不超过 10000。

思路:通过数据,可以预估该题时间复杂度,初步预估O(nlogn),不过发现二分啥的都解不了。思考O(n * n)思路。发现常数不大,完全没问题。

思路一:(dp数数)

数数题,一般思路,遍历一遍数组,在 i 位置时统计添加一个 a[i] 元素,会新增多少个满足答案的区间。

例:3 2 1 4 5

i = 1时,新增 1 个区间。[1, 1]

i = 2时,新增 1 个区间。[2, 2]

i = 3时,新增 2 个区间。[1, 3], [3, 3]

i = 4时,新增 1 个区间。[4, 4]

i = 5 时,不就新增了 2 个区间满足条件。[3, 5],[5, 5]

再思考,如何一次统计出前面所有满足条件的区间呢。

例:1 2 3 4 5 6 7

i = 7时,不就有四个区间可能会提升答案。[7, 7], [5, 7], [3, 7], [1, 7]。

那么可以将题目转化为,统计这些区间中有多少个中位数是在中间。

所以我们可以遍历这些区间的中间的数,判断是不是对应区间的中位数即可。

再思考,那么该如何判断 区间中任意一个数 x 是一个区间的中位数呢?

大于 x 的数的个数 = 小于 x 的数的个数 《=》 x 是这个区间的中位数

那么只需要遍历一遍这个区间,大于 x,则sum ++。小于 x ,则sum --。若sum = 0,表示x就是这个区间的中位数。

所以我们一个数组去动态的预处理 这些区间中间的数的权值,然后遍历一遍判断 是否等于 0, 若等于 0,则ans ++。

例:3 4 2 1 5 6 7

i = 7时,可能提升答案的四个区间为:[1, 7], [3, 7], [5, 7], [7, 7]。

对于[1, 7]区间:中间的位置为4,值为 1 ,其他数都比 1 大,所以 sum[4] = 6;

对于[3, 7]区间:中间的位置为5,值为5,2和1比5小,6和7比5大,所以sum[5] = 0;

对于[5, 7]区间:中间的位置为6,值为6,同理sum[6] = 0;

对于[7, 7]区间:中间的位置为7,值为7,同理sum[7] = 0;

所以预处理后的这个数组值为:

0 0 0 6 0 0 0

其中有三个中间位置的权值为 0 ,估 ans += 3。

那么该如何动态进行维护这个权值数组呢,可以自行思考下,实现思路在代码里有解释。

含有注释代码:

#include <bits/stdc++.h>
using namespace std;

void solve(){
    int n;
    cin >> n;
    vector<int>a(n + 1, 0);

    for(int i = 1; i <= n; i ++){
        cin >> a[i];
    }
    int ans = n; // 提前预处理出长度为 1 的区间
    vector<int>sum(n + 1, 0);
    for(int i = 1; i <= n; i ++){ // 按照上面思路,每次统计新增一个a[i]元素,ans会增加多少。
        
        for(int j = 1; j <= (i - 1) / 2; j ++){ // 遍历 i位置 前面可能提升答案的区间的中间的数,i - j 就为一个区间的中间数
            // 判断新增的a[i], 对这些中间数权值的改变
            if(a[i - j] < a[i]){
                sum[i - j] ++;
            }else if(a[i - j] > a[i]){
                sum[i - j] --;
            }
            
            // 要是这个中间数的权值 == 0,则ans ++
            if(sum[i - j] == 0){
                ans ++;
            }
        }
        
        // 区间[1, (i + 1) / 2] 的值 对 区间[i / 2, i - 1] 权值的更新。
        // 例:1, 2, 3, 4, 5, 6, 7 当 i = 6, 就需要预处理[1, 3]区间对[4, 6]区间权值的更新。
        // 例:1, 2, 3, 4, 5, 6    当 i = 5, 就需要预处理[2, 3]区间对[4, 5]区间权值的更新。
        //                                   而位置1 对 位置 3的更新并不影响后续结果,因为后续将不会使用到这个中间数。

        // 前面数对后面中间数的权值更新,通过每次更新一部分使得成立。
        // 可自行模拟这个过程理解。上面例子只是直观的感受更新区间的相对位置,建议从小的模拟。
        for(int j = 1; j <= (i + 1) / 2; j ++){
            int k = i / 2;
            if(a[j] < a[j + k]){
                sum[j + k] --;
            } else if(a[j] > a[j + k]){
                sum[j + k] ++;
            }
        }
    }

    cout << ans << endl;
}

int main() {
    int t;
    cin >> t;
    while(t --) solve();
}

无注释代码:

#include <bits/stdc++.h>
using namespace std;

void solve(){
    int n;
    cin >> n;
    vector<int>a(n + 1, 0);

    for(int i = 1; i <= n; i ++){
        cin >> a[i];
    }
    int ans = n; 
    vector<int>sum(n + 1, 0);
    for(int i = 1; i <= n; i ++){
        
        for(int j = 1; j <= (i - 1) / 2; j ++){ 
            if(a[i - j] < a[i]){
                sum[i - j] ++;
            }else if(a[i - j] > a[i]){
                sum[i - j] --;
            }

            if(sum[i - j] == 0){
                ans ++;
            }
        }
        
        for(int j = 1; j <= (i + 1) / 2; j ++){
            int k = i / 2;
            if(a[j] < a[j + k]){
                sum[j + k] --;
            } else if(a[j] > a[j + k]){
                sum[j + k] ++;
            }
        }
    }

    cout << ans << endl;
}

int main() {
    int t;
    cin >> t;
    while(t --) solve();
}

思路二:(中心扩展)

#include <bits/stdc++.h>
using namespace std;

int suan(int x, int y){
    if(x > y) return 1;
    else return -1;
}

void solve(){
    int n;
    cin >> n;
    vector<int>a(n + 1, 0);

    for(int i = 1; i <= n; i ++){
        cin >> a[i];
    }
    int ans = n; // 提前预处理出长度为 1 的区间
    for(int i = 1; i <= n; i ++){
        int sum = 0;
        for(int j = 1; i - j >= 1 && i + j <= n; j ++){
            int l = i - j, r = i + j;
            sum += suan(a[i], a[l]) + suan(a[i], a[r]);
            if(sum == 0){
                ans ++;
            }
        }
    }

    cout << ans << endl;
}

int main() {
    int t;
    cin >> t;
    while(t --) solve();
}

#数据结构与算法面试常考题##算法##美团##笔试##软件开发笔面经#
全部评论
第二题我的思路是在L,...,x,...,R区间中找大于 x 的数的个数,然后判断比x大的数是否等于(R-L)/2,然后测试用例过了,提交0%
2 回复 分享
发布于 03-22 23:31 四川
第三题java一直超时,燃尽了
点赞 回复 分享
发布于 03-23 00:50 江苏
要考虑重复数字吗?
点赞 回复 分享
发布于 昨天 01:31 美国

相关推荐

评论
5
7
分享

创作者周榜

更多
牛客网
牛客企业服务