左神算法之双集合平均值优化操作的最大次数
目录
- 1. 题目
- 2. 解释
- 3. 思路
- 4. 代码
- 5. 总结
1. 题目
给定两个整数集合 a
和 b
,定义 magic操作 为:
- 从其中一个集合取出一个元素,放入另一个集合。
- 操作后,两个集合的平均值都必须严格大于操作前的平均值。
限制条件:
- 不能将任一集合取空(否则无法计算平均值)。
- 如果被移动的元素
x
在目标集合中已存在,则目标集合的平均值不变(因为集合元素不重复),但源集合的平均值可能改变(因为x
被移除)。
问题: 最多可以进行多少次这样的 magic 操作?
2. 解释
-
Magic操作的条件:
- 设从集合
A
移动元素x
到集合B
,则必须满足:A
的新平均值 >A
的旧平均值。B
的新平均值 >B
的旧平均值。
- 由于集合元素不重复,如果
x
已在B
中存在,则B
的平均值不变(因为B
的元素不变),此时只需A
的新平均值 >A
的旧平均值。
- 设从集合
-
关键观察:
- 如果
avg(A) == avg(B)
,无法进行任何 magic 操作(因为移动后至少一个集合的平均值会减小)。 - 如果
avg(A) > avg(B)
,只能从A
移动 严格小于avg(A)
且严格大于avg(B)
的元素到B
,且B
中不能已有该元素。
- 如果
3. 思路
-
计算初始平均值:
- 计算
avgA = sum(A) / |A|
,avgB = sum(B) / |B|
。 - 如果
avgA == avgB
,直接返回0
(无法操作)。
- 计算
-
确定移动方向:
- 假设
avgA > avgB
,则只能从A
移动元素到B
。 - 移动的元素
x
必须满足:avgB < x < avgA
(保证移动后A
和B
的平均值都增大)。x
不在B
中(否则B
的平均值不变,无法满足B
的新平均值 > 旧平均值)。
- 假设
-
贪心策略:
- 对
A
排序,从小到大检查可以移动的元素。 - 每次移动后,更新
sumA
、sumB
、|A|
、|B|
和avgA
、avgB
。 - 重复直到无法移动为止。
- 对
4. 代码
public class Problem02_Magic0p {// 保证arr1和arr2均没有重复值哦,且一定有数字public static int maxOps(int[] arr1, int[] arr2) {double sum1 = 0;for (int i = 0; i < arr1.length; i++) {sum1 += (double) arr1[i];}double sum2 = 0;for (int i = 0; i < arr2.length; i++) {sum2 += (double) arr2[i];}if(avg(sum1, arr1.length) == avg(sum2, arr2.length)){return 0;}// 平均值不一样int[] arrMore = null;int[] arrLess = null;double sumMore = 0;double sumLess = 0;if(avg(sum1, arr1.length) > avg(sum2, arr2.length)){arrMore = arr1;sumMore = sum1;arrLess = arr2;sumLess = sum2;}else{arrMore = arr2;sumMore = sum2;arrLess = arr1;sumLess = sum1;}Arrays.sort(arrMore);HashSet<Integer> setLess = new HashSet<>();for(int num : arrLess){setLess.add(num);}int moreSize = arrMore.length; // 平均值大的集合还剩几个数int lessSize = arrLess.length; // 平均值小的集合还剩几个数int ops = 0; // 移动次数for(int i = 0; i < arrMore.length; i++){double cur = (double) arrMore[i];if(cur < avg(sumMore, moreSize) && cur > avg(sumLess, lessSize) && !setLess.contains(arrMore[i])){sumMore -= cur;moreSize--;sumLess += cur;lessSize++;setLess.add(arrMore[i]);ops++;}}return ops;}public static double avg(double sum, int length){return sum / length;}public static int sum(int[] arr){int sum = 0;for(int i = 0; i < arr.length; i++){sum += arr[i];}return sum;}public static void main(String[] args) {int[] arr1 = {1, 2, 3};System.out.println(avg(sum(arr1), arr1.length));int[] arr2 = {4, 5, 6};System.out.println(avg(sum(arr2), arr2.length));System.out.println(maxOps(arr1, arr2));}
}
输出结果:
2.0
5.0
2
5. 总结
- 核心逻辑:
- 确保
avg(A) > avg(B)
,然后从A
移动满足avg(B) < x < avg(A)
且不在B
中的元素。 - 每次移动后更新
sum
和avg
,继续检查能否移动更多元素。
- 确保
- 时间复杂度:
- 排序
A
的时间为O(n log n)
,遍历A
的时间为O(n)
,总体O(n log n)
。
- 排序
- 适用场景:
- 适用于两个集合平均值不相等且存在可移动元素的情况。
- 如果
avg(A) == avg(B)
或没有满足条件的x
,则无法操作。