题目描述与示例
题目描述
一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0~N
的箱子,每个箱子上面贴有箱子中藏有金币的数量。
从金币数量中选出一个数字集合,并销毁贴有这些数字的每个箱子,如果能销毁一半及以上的箱子,则返回这个数字集合的最小大小。
输入描述
一个数字字串,数字之间使用逗号分隔,例如: 6,6,6,6,3,3,3,1,1,5
字符串中数字的个数为偶数;
1 <= 数字个数 <=100000
;
1 <= ``数字`` ``<=100000
。
输出描述
这个数字集合的最小大小,例如: 2
示例一
输入
1,1,1,1,3,3,3,6,6,8
输出
2
说明
选择集合{1,8}
,销毁后的结果数组为[3,3,3,6,6]
,长度为 5
,长度为原数组的一半。
大小为2
的可行集合还有{1,3},{1,6},{3,6}
。
选择6,8
集合是不可行的,它销毁后的结果数组为[1,1,1,1,3,3,3]
,新数组长度大于原数组的二分之一。
示例二
输入
2,2,2,2
输出
1
说明
我们只能选择集合{2}
,销毁后的结果数组为空
解题思路
贪心地思考问题,优先选择出现次数多的箱子进行销毁即可。统计同种箱子出现次数,需要用到计数器Counter
。
代码
Python
# 题目:2023B-阿里巴巴找黄金宝箱2
# 分值:100
# 作者:闭着眼睛学数理化
# 算法:贪心
# 代码看不懂的地方,请直接在群上提问
from collections import Counter
from math import ceil
# 输入所有数字
lst = list(map(int, input().split(",")))
# 统计数字出现的次数
cnt = Counter(lst)
# 逆序排序所有数字出现次数,用于后续的贪心过程
nums = list(sorted(cnt.values(), reverse = True))
# 需要挑出的目标和,为lst长度的一半(向上取整)
target = ceil(len(lst) / 2)
# 已经挑出的数字的个数
total = 0
# 答案变量
ans = 0
# 遍历nums中的每一个数字num
for num in nums:
# 将num加入total中
total += num
# 选择了一种集合,ans递增
ans += 1
# 如果加完num后,total超过了目标和,输出答案ans,同时退出循环
if total >= target:
print(ans)
break
Java
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String[] input = scanner.nextLine().split(",");
int[] lst = Arrays.stream(input).mapToInt(Integer::parseInt).toArray();
Map<Integer, Integer> cnt = new HashMap<>();
for (int num : lst) {
cnt.put(num, cnt.getOrDefault(num, 0) + 1);
}
int[] nums = cnt.values().stream().mapToInt(Integer::intValue).toArray();
Arrays.sort(nums);
int target = (int) Math.ceil(lst.length / 2.0);
int total = 0;
int ans = 0;
for (int i = nums.length - 1; i >= 0; i--) {
total += nums[i];
ans++;
if (total >= target) {
System.out.println(ans);
break;
}
}
}
}
C++
“`C++
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
using namespace std;
int main() {
string input;
getline(cin, input);
<pre><code>vector<string> inputTokens;
size_t pos = 0;
while ((pos = input.find(",")) != string::npos) {
inputTokens.push_back(input.substr(0, pos));
input.erase(0, pos + 1);
}
inputTokens.push_back(input);
vector<int> lst;
for (const string& token : inputTokens) {
lst.push_back(stoi(token));
}
map<int, int> cnt;
for (int num : lst) {
cnt[num]++;
}
vector<int> nums;
for (const auto& kv : cnt) {
nums.push_back(kv.second);
}
sort(nums.rbegin(), nums.rend());
int target = ceil(static_cast<double>(lst.size()) / 2);
int total = 0;
int ans = 0;
for (int num : nums) {
total += num;
ans++;
if (total >= target) {
cout << ans << endl;
break;
}
}
return 0;
</code></pre>
}
“`
时空复杂度
时间复杂度:O(``NlogN``)
。排序的时间复杂度。
空间复杂度:O(``1``)
。仅需若干常数变量。
说明
华为OD机试有三道题⽬,第⼀道和第⼆道属于简单或中等题,分值为 100 分,第三道为中等或困难题,分值为 200分,总分为 400 分。
机试分数越⾼评级越⾼,⼯资也就越⾼。
关于华为 OD 机试更加详细的介绍可以查看这篇⽂章:华为OD机考须知
关于机考题目汇总可以看这篇文章:华为OD机试真题 2023 A+B+C+D卷 + 2024新卷(Python&Java&C++)⽬录汇总(每⽇更新)