结队编程 - 华为OD统一考试

OD统一考试

题解: Java / Python / C++

题目描述

某部门计划通过结队编程来进行项目开发,已知该部门有 N 名员工,每个员工有独一无二的职级,每三个员工形成一个小组进行结队编程,结队分组规则如下:

从部门中选出序号分别为 i、j、k 的3名员工,他们的职级分别为 level[i],level[j],level[k],结队小组满足 level[i] < level[j] < level[k] 或者 level[i] > level[j] > level[k],其中 0 ≤ i < j < k < n。

请你按上述条件计算可能组合的小组数量。同一员工可以参加多个小组。

输入描述

第一行输入:员工总数 n

第二行输入:按序号依次排列的员工的职级 level,中间用空格隔开

备注:

1 <= n <= 6000

1 <= level[i] <= 10^5

输出描述

可能结队的小组数量

示例1

输入:
4
1 2 3 4

输出:
4

说明:
可能结队成的组合(1,2,3)、(1,2,4)、(1,3,4)、(2,3,4)

示例2

输入:
3
5 4 7

输出:
0

说明:
根据结队条件,我们无法为该部门组建小组

题解

使用线段树 的解法,该解法通过统计每个员工左边比他小的员工数(lt数组),以及右边比他大的员工数(gt数组),然后利用线段树来快速统计某个区间内的数目,最终计算可能的结队小组数量。

具体步骤如下:

  1. 对每个员工,使用线段树统计他左边比他小的员工数(lt数组)和右边比他大的员工数(gt数组)。
  2. 遍历员工,对于每个员工,计算可能的结队小组数量,累加到结果中。
  3. 输出最终的结果。

这个解法的时间复杂度为O(N log N),其中N是员工的数量。这是因为在线段树的构建和查询过程中,每个员工都要进行O(log N)的操作。空间复杂度也为O(N),用于存储线段树的数据结构。

这个解法在处理大规模数据时具有较好的性能,但对于小规模数据,可能会有一些冗余。

对线段树不了解,可以通过 https://www.bilibili.com/video/BV1QT4y1Z7rR 视频来学习。

Java

java 复制代码
import java.util.Scanner;
/**
 * @author code5bug
 */
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] levels = new int[n];
        for (int i = 0; i < n; i++) levels[i] = in.nextInt();

        final int LEFT_BOUND = 1, RIGHT_BOUND = 100000;

        // lt[i] 是 levels[0:i] 中小于 levels[i] 的个数
        // gt[i] 是 levels[0:i] 中大于 levels[i] 的个数
        int[] lt = new int[n], gt = new int[n];
        SegmentTree segTree = new SegmentTree(LEFT_BOUND, RIGHT_BOUND);
        for (int i = 0; i < n; i++) {
            lt[i] = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
            gt[i] = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
            segTree.insert(levels[i]);
        }

        long rs = 0;
        for (int i = 1; i < n - 1; i++) {
            // 数组中总 [1, level[i] - 1] 总个数
            int total_lt = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
            // 数组中总 [level[i] + 1, RIGHT_BOUND] 总个数
            int total_gt = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
            rs += lt[i] * (total_gt - gt[i]);
            rs += gt[i] * (total_lt - lt[i]);
        }

        System.out.println(rs);
    }
}


/**
 * 线段树
 */
class SegmentTree {
    private int leftBound, rightBound;
    private int[] data;

    public SegmentTree(int leftBound, int rightBound) {
        this.leftBound = leftBound;
        this.rightBound = rightBound;

        int size = (rightBound - leftBound + 1) * 4;
        this.data = new int[size];
    }

    public void insert(int num) {
        insert(0, leftBound, rightBound, num);
    }

    public void insert(int pos, int left, int right, int num) {
        this.data[pos]++;

        //线段树叶子节点
        if (left == num && right == num) {
            return;
        }

        int mid = left + (right - left) / 2;
        if (num <= mid) {
            insert(pos * 2 + 1, left, mid, num);
        } else {
            insert(pos * 2 + 2, mid + 1, right, num);
        }
    }

    public int searchRange(int minNum, int maxNum) {
        return searchRange(0, leftBound, rightBound, minNum, maxNum);
    }

    /**
     * 数值范围查询
     *
     * @param pos
     * @param left
     * @param right
     * @param minNum
     * @param maxNum
     * @return
     */
    public int searchRange(int pos, int left, int right, int minNum, int maxNum) {
        // 查询范围外,没有结果
        if (minNum > right || maxNum < left) {
            return 0;
        }

        // 叶子节点
        if (left == right) {
            return data[pos];
        }

        if (minNum <= left && maxNum >= right) {
            // 完全被包含在查询范围内
            return data[pos];
        } else {
            int mid = left + (right - left) / 2;
            return searchRange(pos * 2 + 1, left, mid, minNum, maxNum) + searchRange(pos * 2 + 2, mid + 1, right, minNum, maxNum);
        }
    }
}

Python

python 复制代码
class SegmentTree:
    def __init__(self, left_bound, right_bound):
        self.left_bound = left_bound
        self.right_bound = right_bound
        size = (right_bound - left_bound + 1) * 4
        self.data = [0] * size

    def insert(self, num):
        self._insert(0, self.left_bound, self.right_bound, num)

    def _insert(self, pos, left, right, num):
        self.data[pos] += 1
        if left == right:
            return
        mid = left + (right - left) // 2
        if num <= mid:
            self._insert(pos * 2 + 1, left, mid, num)
        else:
            self._insert(pos * 2 + 2, mid + 1, right, num)

    def search_range(self, min_num, max_num):
        return self._search_range(0, self.left_bound, self.right_bound, min_num, max_num)

    def _search_range(self, pos, left, right, min_num, max_num):
        if min_num > right or max_num < left:
            return 0
        if left == right:
            return self.data[pos]
        if min_num <= left and max_num >= right:
            return self.data[pos]
        mid = left + (right - left) // 2
        return self._search_range(pos * 2 + 1, left, mid, min_num, max_num) + self._search_range(pos * 2 + 2, mid + 1, right, min_num, max_num)


def main():
    n = int(input())
    levels = list(map(int, input().split()))
    LEFT_BOUND, RIGHT_BOUND = 1, 100000

    lt = [0] * n
    gt = [0] * n
    seg_tree = SegmentTree(LEFT_BOUND, RIGHT_BOUND)

    for i in range(n):
        lt[i] = seg_tree.search_range(LEFT_BOUND, levels[i] - 1)
        gt[i] = seg_tree.search_range(levels[i] + 1, RIGHT_BOUND)
        seg_tree.insert(levels[i])

    rs = 0
    for i in range(1, n - 1):
        total_lt = seg_tree.search_range(LEFT_BOUND, levels[i] - 1)
        total_gt = seg_tree.search_range(levels[i] + 1, RIGHT_BOUND)
        rs += lt[i] * (total_gt - gt[i])
        rs += gt[i] * (total_lt - lt[i])

    print(rs)


if __name__ == "__main__":
    main()

C++

cpp 复制代码
#include <iostream>
#include <vector>

using namespace std;

class SegmentTree {
private:
    int leftBound, rightBound;
    vector<int> data;

public:
    SegmentTree(int left, int right) : leftBound(left), rightBound(right) {
        int size = (rightBound - leftBound + 1) * 4;
        data.resize(size, 0);
    }

    void insert(int num) {
        insert(0, leftBound, rightBound, num);
    }

    void insert(int pos, int left, int right, int num) {
        data[pos]++;

        if (left == num && right == num) {
            return;
        }

        int mid = left + (right - left) / 2;
        if (num <= mid) {
            insert(pos * 2 + 1, left, mid, num);
        } else {
            insert(pos * 2 + 2, mid + 1, right, num);
        }
    }

    int searchRange(int minNum, int maxNum) {
        return searchRange(0, leftBound, rightBound, minNum, maxNum);
    }

    int searchRange(int pos, int left, int right, int minNum, int maxNum) {
        if (minNum > right || maxNum < left) {
            return 0;
        }

        if (left == right) {
            return data[pos];
        }

        if (minNum <= left && maxNum >= right) {
            return data[pos];
        } else {
            int mid = left + (right - left) / 2;
            return searchRange(pos * 2 + 1, left, mid, minNum, maxNum) +
                   searchRange(pos * 2 + 2, mid + 1, right, minNum, maxNum);
        }
    }
};

int main() {
    int n;
    cin >> n;

    const int LEFT_BOUND = 1, RIGHT_BOUND = 100000;
    vector<int> levels(n);
    for (int i = 0; i < n; ++i) {
        cin >> levels[i];
    }

    vector<int> lt(n), gt(n);
    SegmentTree segTree(LEFT_BOUND, RIGHT_BOUND);

    for (int i = 0; i < n; ++i) {
        lt[i] = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
        gt[i] = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
        segTree.insert(levels[i]);
    }

    long long result = 0;
    for (int i = 1; i < n - 1; ++i) {
        int total_lt = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
        int total_gt = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
        result += lt[i] * (total_gt - gt[i]) + gt[i] * (total_lt - lt[i]);
    }

    cout << result << endl;

    return 0;
}

相关练习题

题号 题目 难易
LeetCode 307 307. 区域和检索 - 数组可修改 中等
LeetCode 2276 2276. 统计区间中的整数数目 困难

🙏整理题解不易, 如果有帮助到您,请给点个赞 ‍❤️‍ 和收藏 ⭐,让更多的人看到。🙏🙏🙏

相关推荐
Swift社区20 分钟前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman1 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
阿龟在奔跑1 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF1 小时前
m个数 生成n个数的所有组合 详解
java·递归
一念之坤1 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python
代码小鑫1 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖1 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶1 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
IT 青年1 小时前
数据结构 (1)基本概念和术语
数据结构·算法