可上 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳 od1441了解算法冲刺训练(备注【CSDN】否则不通过)

相关推荐阅读

题目练习网址:【哈希表】2025C-符合要求的结对方式

题目描述与示例

题目描述

用一个数组A代表程序员的工作能力,公司想通过结对编程的方式提高员工的能力,假设结对后的能力为两个员工的能力之和,求一共有多少种结对方式使结对后能力为N

输入描述

第一行为员工的总人数,取值范围[1,1000]

第二行为数组A的元素,每个元素的取值范围[1,1000]

第三行为N的值,取值范围[1,1000]

输出描述

满足结对后能力为N的结对方式总数。

示例

输入

5
1 2 2 2 3
4

输出

4

说明

满足要求的结对方式为:A[0]A[4]A[1]A[2]A[1]A[3]A[2]A[3]

解题思路

本题类似于求配对组数版本的LC1. 两数之和

容易想到,我们可以使用哈希表计数器Counter()来储存所有数字以及它们出现的频次。

假设存在于A中的两个数字存在num1 + num2 = N成立,我们分情况讨论。若

  • num1 != num2,它们出现的频率分别是cnt[num1]cnt[num2],根据乘法原理,它们一共能够构成cnt[num1]*cnt[num2]组不同的组合。
  • num1 = num2 = num,则构成的组数相当于在cnt[num] = mnum中挑选出2num进行组合,即可以构成 C m 2 = m ( m − 1 ) 2 C_m^2 = \frac{m(m-1)}{2} Cm2=2m(m1)组不同的组合

再根据加法原理将所有组合数加起来即可。

在代码实现上,如果我们遍历cnt中的所有key,当num1 != num2时会导致重复计算。

所以我们可以额外加一个判断,只考虑所有小于等于N / 2的数字即可。

代码

Python

# 欢迎来到「欧弟算法 - 华为OD全攻略」,收录华为OD题库、面试指南、八股文与学员案例!
# 地址:https://www.odalgo.com
# 华为OD机试刷题网站:https://www.algomooc.com
# 添加微信 278166530 获取华为 OD 笔试真题题库和视频

# 题目:【哈希表】2025C-符合要求的结对方式
# 分值:100
# 作者:许老师-闭着眼睛学数理化
# 算法:哈希表
# 代码看不懂的地方,请直接在群上提问

from collections import Counter

# 员工总人数
m = int(input())
# 输入员工能力值
nums = list(map(int, input().split()))
# 输入结队目标值
N = int(input())

# 统计每个能力值出现的次数
cnt = Counter(nums)
# 统计符合要求的结对方式数量,初始化为0
ans = 0

# 遍历所有能力值
for k in cnt:
    # 仅考虑小于等于N / 2的能力值k
    # 若k小于N/2,则k和N-k可以组成一对
    if k < N / 2:
        # 共增加cnt[k] * cnt[N-k]组
        ans += cnt[k] * cnt[N-k]
    # 若k等于N/2,则k只能和自身组成一对
    elif k == N / 2:
        # 共增加cnt[k] * (cnt[k] - 1) // 2组
        ans += cnt[k] * (cnt[k] - 1) // 2

print(ans)

Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // 员工总人数
        int m = sc.nextInt();
        // 员工能力值
        int[] nums = new int[m];
        for (int i = 0; i < m; i++) nums[i] = sc.nextInt();
        // 结队目标值
        int N = sc.nextInt();

        // 统计每个能力值出现次数
        Map<Integer, Integer> cnt = new HashMap<>();
        for (int x : nums) cnt.put(x, cnt.getOrDefault(x, 0) + 1);

        long ans = 0L;
        for (int k : cnt.keySet()) {
            int t = N - k;
            if (k < t) { // 只从较小的 k 这一侧计数,避免重复
                long c1 = cnt.get(k);
                long c2 = cnt.getOrDefault(t, 0);
                ans += c1 * c2;
            } else if (k == t) { // 组内两两配对
                long c = cnt.get(k);
                ans += c * (c - 1) / 2;
            }
            // k > t 的情况跳过(已在 t 那一侧统计过)
        }

        System.out.println(ans);
        sc.close();
    }
}

C++

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // 员工总人数
    int m; 
    if (!(cin >> m)) return 0;

    // 员工能力值
    vector<int> nums(m);
    for (int i = 0; i < m; ++i) cin >> nums[i];

    // 结队目标值
    long long N;
    cin >> N;

    // 统计每个能力值出现次数
    unordered_map<long long, long long> cnt; 
    cnt.reserve(m * 2);
    for (int x : nums) ++cnt[x];

    long long ans = 0;
    for (const auto& kv : cnt) {
        long long k = kv.first;
        long long c1 = kv.second;
        long long t = N - k;

        if (k < t) {
            auto it = cnt.find(t); 
            if (it != cnt.end()) {
                ans += c1 * it->second;
            }
        } else if (k == t) {
            ans += c1 * (c1 - 1) / 2;
        }
        // k > t 跳过,避免重复
    }

    cout << ans << "\n";
    return 0;
}

C

#include <stdio.h>
#include <stdlib.h>

/* 快速比较函数:用于 qsort 将数组按升序排序 */
static int cmp_ll(const void *a, const void *b) {
    long long x = *(const long long*)a;
    long long y = *(const long long*)b;
    return (x < y) ? -1 : (x > y);
}

int main(void) {
    // 员工总人数
    int m;
    if (scanf("%d", &m) != 1) return 0;

    // 读取员工能力值(用 long long 存,兼容大值/负数)
    long long *arr = (long long*)malloc(sizeof(long long) * (size_t)m);
    if (!arr) return 1;

    for (int i = 0; i < m; ++i) {
        long long v;
        scanf("%lld", &v);
        arr[i] = v;
    }

    // 结队目标值 N
    long long N;
    scanf("%lld", &N);

    // 排序后用双指针统计所有 (i<j) 且 arr[i]+arr[j]==N 的配对数
    qsort(arr, (size_t)m, sizeof(long long), cmp_ll);

    long long ans = 0;
    int l = 0, r = m - 1;

    while (l < r) {
        long long sum = arr[l] + arr[r];
        if (sum < N) {
            ++l;
        } else if (sum > N) {
            --r;
        } else {
            // sum == N
            if (arr[l] == arr[r]) {
                // 区间内元素都相等:从 l 到 r 的任取两两配对
                long long k = (long long)(r - l + 1);
                ans += k * (k - 1) / 2;
                break; // 全部处理完毕
            } else {
                // 统计左侧相同值的个数 c1,右侧相同值的个数 c2
                long long leftVal = arr[l], rightVal = arr[r];
                long long c1 = 0, c2 = 0;
                while (l <= r && arr[l] == leftVal) { ++c1; ++l; }
                while (r >= l && arr[r] == rightVal) { ++c2; --r; }
                ans += c1 * c2; // 左组与右组的任意组合
            }
        }
    }

    printf("%lld\n", ans);

    free(arr);
    return 0;
}

Node JavaScript

// 使用 Node.js 的 Map 来统计频率
const readline = require("readline");

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let input = [];
rl.on("line", line => {
    input.push(line.trim());
});

rl.on("close", () => {
    // 员工总人数
    let m = parseInt(input[0]);
    // 员工能力值
    let nums = input[1].split(" ").map(Number);
    // 结队目标值
    let N = parseInt(input[2]);

    // 统计每个能力值出现次数
    let cnt = new Map();
    for (let x of nums) {
        cnt.set(x, (cnt.get(x) || 0) + 1);
    }

    let ans = 0n; // 使用 BigInt 避免大数溢出

    for (let [k, c1] of cnt.entries()) {
        let t = N - k;
        if (k < t) {
            let c2 = BigInt(cnt.get(t) || 0);
            ans += BigInt(c1) * c2;
        } else if (k === t) {
            let c = BigInt(c1);
            ans += c * (c - 1n) / 2n;
        }
        // k > t 的情况跳过
    }

    console.log(ans.toString());
});

Go

package main

import (
        "bufio"
        "fmt"
        "os"
        "strconv"
        "strings"
)

func main() {
        in := bufio.NewReader(os.Stdin)

        // 员工总人数
        line, _ := in.ReadString('\n')
        m, _ := strconv.Atoi(strings.TrimSpace(line))

        // 员工能力值
        line, _ = in.ReadString('\n')
        parts := strings.Fields(line)
        nums := make([]int, m)
        for i := 0; i < m; i++ {
                nums[i], _ = strconv.Atoi(parts[i])
        }

        // 结队目标值
        line, _ = in.ReadString('\n')
        N, _ := strconv.Atoi(strings.TrimSpace(line))

        // 统计每个能力值出现次数
        cnt := make(map[int]int)
        for _, x := range nums {
                cnt[x]++
        }

        var ans int64 = 0
        for k, c1 := range cnt {
                t := N - k
                if k < t {
                        if c2, ok := cnt[t]; ok {
                                ans += int64(c1) * int64(c2)
                        }
                } else if k == t {
                        c := int64(c1)
                        ans += c * (c - 1) / 2
                }
                // k > t 跳过
        }

        fmt.Println(ans)
}

时空复杂度

时间复杂度:O(m)。构建哈希表和遍历哈希表所需的时间复杂度。

空间复杂度:O(m)。哈希表所占空间。


华为OD算法/大厂面试高频题算法练习冲刺训练

  • 华子OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务1000+同学成功上岸!

  • 课程讲师为全网200w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

  • 90+天陪伴式学习,100+直播课时,300+动画图解视频,500+LeetCode经典题,500+华为OD真题/大厂真题,还有简历修改、模拟面试、陪伴小群、资深HR对接将为你解锁

  • 可上全网独家的欧弟OJ系统练习华子OD、大厂真题

  • 可查看链接OD真题汇总(持续更新)

  • 绿色聊天软件戳 od1441或了解更多

更多推荐