题目描述与示例
题目描述
现有两组服务器A
和B
,每组有多个算力不同的CPU,其中Ai
是A
组第i
个CPU的运算能力,Bi
是B
组第i
个CPU的运算能力。一组服务器的总算力是各CPU的算力之和。
为了让两组服务器的算力相等,允许从每组各选出一个CPU进行一次交换,求两组服务器中,用于交换的CPU的算力,并且要求从A
组服务器中选出的CPU,算力尽可能小。
输入描述
第一行输入为L1
和L2
,以空格分隔,L1
表示A
组服务器中的CPU数量,L2
表示B
组服务器中的CPU数量
第二行输入为A
组服务器中各个CPU的算力值,以空格分隔。
第三行输入为B
组服务器中各个CPU的算力值,以空格分隔。
1 <= L1 <= 10000
1 <= L2 <= 10000
1 <= A[i] <= 100000
1 <= B[i] <= 100000
输出描述
对于每组测试数据,输出两个整数,以空格分隔,依次表示A
组选出的CPU算力、B
组选出的CPU算力,要求从A
组选出的CPU的算力尽可能小。
补充说明
保证两组服务器的初始总算力不同,答案肯定存在。
示例一
输入
2 2
1 1
2 2
输出
1 2
示例二
输入
3 4
1 2 3
1 2 3 4
输出
1 3
说明
有两种可能的选择,选择A
组中的1
和B
组中的3
进行交换,或者选择A
组中的2
和B
组中的4
进行交换,但由于要求A
组选择的算力要尽可能地小,所以选择前者。
解题思路
注意有两个非常重要的条件:
- 允许从每组各选出一个CPU进行一次交换
- 保证两组服务器的初始总算力不同,答案肯定存在
第一个条件指出,只能在A
组和B
组中各挑选出1
个数据,不用考虑多组交换的情况
第二个条件指出,无需考虑不合规则的情况,必然能够找到一个A[i]
和一个B[j]
,交换后使得各自的和相等。
假设原A
组的和为sumA
,原B
组的和为sumB
,所有CPU的算力总和为sum_total
,取出的两个用于交换的元素分别为A[i]
和B[j]
。
sumA = sum(A)
sumB = sum(B)
sum_total = sumA + sumB
交换后,如果A
组的和为sumA_new
,B
组的和为sumB_new
,那么一定存在以下式子成立
sumA_new = sumB_new = sum_total // 2
且sum_total
一定是一个偶数。
sumA_new = sum_total // 2
A`组少了一个`A[i]`多了一个`B[j]`,故交换前后的变化量为`sumA_new - sumA = B[j] - A[i]
即如果交换后A
组和B
组的算力总和相等,存在式子B[j] = sumA_new - sumA + A[i]
成立。
因此,我们只需要遍历A
组中所有的元素A[i]
,然后计算对应的sumA_new - sumA + A[i]
,如果该值位于B
组中,说明B
组中可以找到对应的B[j]
。
这个步骤涉及到快速查找,因此很容易想到应该构建哈希集合setB = set(B)
。
从示例二可以得知,可以交换的组数不是唯一的。由于题目还要求从A
组服务器中选出的CPU,算力尽可能小,因此仅需把所有符合条件的A[i]
取最小值即可。
另外,由于A
组中可能出现重复元素,重复元素的计算对于本问题而言是无意义的,因此也可以构建setA = set(A)
,遍历A组元素时直接遍历setA
而非A
即可。
代码
Python
# 题目:【哈希集合】2023C-CPU算力分配
# 分值:100
# 作者:闭着眼睛学数理化
# 算法:哈希集合,数学
# 代码看不懂的地方,请直接在群上提问
# 输入A组的长度和B组的长度
nA, nB = map(int, input().split())
# 输入A组的情况
A = list(map(int, input().split()))
# 输入B组的情况
B = list(map(int, input().split()))
# 原A组的总和
sumA = sum(A)
# 原B组的总和
sumB = sum(B)
# 交换后A组的和,为所有元素总和的一半
sumA_new = (sumA + sumB) // 2
# 分别获得A组和B组对应的哈希集合
# setA的作用是去重,setB的作用是快速查找
setA = set(A)
setB = set(B)
# 找到所有满足sumA_new - sumA + Ai位于setB中的Ai的最小值,即为A组中所选的算力ansA
ansA = min(Ai for Ai in setA if (sumA_new - sumA + Ai) in setB)
# 对应地可以计算出ansB
ansB = sumA_new - sumA + ansA
# 输出答案
print(ansA, ansB)
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int nA = scanner.nextInt();
int nB = scanner.nextInt();
int[] A = new int[nA];
int[] B = new int[nB];
for (int i = 0; i < nA; i++) {
A[i] = scanner.nextInt();
}
for (int i = 0; i < nB; i++) {
B[i] = scanner.nextInt();
}
int sumA = 0, sumB = 0;
for (int num : A) {
sumA += num;
}
for (int num : B) {
sumB += num;
}
int sumANew = (sumA + sumB) / 2;
Set<Integer> setB = new HashSet<>();
for (int num : B) {
setB.add(num);
}
int ansA = Integer.MAX_VALUE;
for (int num : A) {
if (setB.contains(sumANew - sumA + num)) {
ansA = Math.min(ansA, num);
}
}
int ansB = sumANew - sumA + ansA;
System.out.println(ansA + " " + ansB);
}
}
C++
“`C++
#include <iostream>
#include <vector>
#include <unordered_set>
#include <algorithm>
#include <climits>
using namespace std;
int main() {
int nA, nB;
cin >> nA >> nB;
vector<int> A(nA), B(nB);
for (int i = 0; i < nA; i++) {
cin >> A[i];
}
for (int i = 0; i < nB; i++) {
cin >> B[i];
}
<pre><code>int sumA = 0, sumB = 0;
for (int num : A) {
sumA += num;
}
for (int num : B) {
sumB += num;
}
int sumANew = (sumA + sumB) / 2;
unordered_set<int> setB(B.begin(), B.end());
int ansA = INT_MAX;
for (int num : A) {
if (setB.count(sumANew – sumA + num)) {
ansA = min(ansA, num);
}
}
int ansB = sumANew – sumA + ansA;
cout << ansA << " " << ansB << endl;
return 0;
</code></pre>
}
“`
时空复杂度
时间复杂度:O(N+M)
。构建两个哈希集合所需的时间复杂度
空间复杂度:O(N+M)
。两个哈希集合所占的空间。
说明
华为OD机试有三道题⽬,第⼀道和第⼆道属于简单或中等题,分值为 100 分,第三道为中等或困难题,分值为 200分,总分为 400 分。
机试分数越⾼评级越⾼,⼯资也就越⾼。
关于华为 OD 机试更加详细的介绍可以查看这篇⽂章:华为OD机考须知
关于机考题目汇总可以看这篇文章:华为OD机试真题 2023 A+B+C+D卷 + 2024新卷(Python&Java&C++)⽬录汇总(每⽇更新)