- 问题描述
给你一个整数数组
arr
。你可以从中选出一个整数集合,并删除这些整数在数组中的每次出现。返回 至少 能删除数组中的一半整数的整数集合的最小大小。
- 示例
输入:arr = [3,3,3,3,5,5,5,2,2,7] 输出:2 解释:选择 {3,7} 使得结果数组为 [5,5,5,2,2]、长度为 5(原数组长度的一半)。 大小为 2 的可行集合有 {3,5},{3,2},{5,2}。 选择 {2,7} 是不可行的,它的结果数组为 [3,3,3,3,5,5,5],新数组长度大于原数组的二分之一。 输入:arr = [7,7,7,7,7,7] 输出:1 解释:我们只能选择集合 {7},结果数组为空。 输入:arr = [1,9] 输出:1 输入:arr = [1000,1000,3,7] 输出:1 输入:arr = [1,2,3,4,5,6,7,8,9,10] 输出:5
- 提示
1 <= arr.length <= 10^5
arr.length
为偶数
1 <= arr[i] <= 10^5
- 解读
一、先统计每个数字的个数,存到数组里 A。 原来数组总长度一半为 S
二、再看这些数组里 A 的子数组的的有没有刚好等于S,如果没有,再看有没有超过一些的。这些循环。
三、按照二的想法,这是一个 0-1 背包问题。 注意到一点, 数组 A 的总个数是一定, 即为2S。 也即是说,不会有这种现象,存在一个子数组,长度超过S。 同时也存在一个子数组,长度 = S。
四、按三的分析,这个最用贪心就可以解决,先把A 从大到小排序 ,遍历累加个数,直到刚好超过 S ,那这个子数组个数就是题意要求的个数
- 代码
public int minSetSize(int[] arr) {
int[] nums = new int[Arrays.stream(arr).summaryStatistics().getMax() + 1];
Arrays.fill(nums, 0);
int len = arr.length / 2;
for (int i = 0; i < arr.length; i++) {
nums[arr[i]]++;
}
// 求nums 里面,有没有和为 len 的
Arrays.sort(nums);
int p = 0;
int t = 0;
for (int i = nums.length - 1; i >= 0; i--) {
p = p + nums[i];
t++;
if (p >= len) {
break;
}
}
return t;
}