左神算法之双集合平均值优化操作的最大次数

目录

  • 1. 题目
  • 2. 解释
  • 3. 思路
  • 4. 代码
  • 5. 总结

1. 题目

给定两个整数集合 ab,定义 magic操作 为:

  • 从其中一个集合取出一个元素,放入另一个集合。
  • 操作后,两个集合的平均值都必须严格大于操作前的平均值

限制条件:

  1. 不能将任一集合取空(否则无法计算平均值)。
  2. 如果被移动的元素 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. 思路

  1. 计算初始平均值:

    • 计算 avgA = sum(A) / |A|avgB = sum(B) / |B|
    • 如果 avgA == avgB,直接返回 0(无法操作)。
  2. 确定移动方向:

    • 假设 avgA > avgB,则只能从 A 移动元素到 B
    • 移动的元素 x 必须满足:
      • avgB < x < avgA(保证移动后 AB 的平均值都增大)。
      • x 不在 B 中(否则 B 的平均值不变,无法满足 B 的新平均值 > 旧平均值)。
  3. 贪心策略:

    • A 排序,从小到大检查可以移动的元素。
    • 每次移动后,更新 sumAsumB|A||B|avgAavgB
    • 重复直到无法移动为止。

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 中的元素。
    • 每次移动后更新 sumavg,继续检查能否移动更多元素。
  • 时间复杂度:
    • 排序 A 的时间为 O(n log n),遍历 A 的时间为 O(n),总体 O(n log n)
  • 适用场景:
    • 适用于两个集合平均值不相等且存在可移动元素的情况。
    • 如果 avg(A) == avg(B) 或没有满足条件的 x,则无法操作。

你可能感兴趣的:(左神算法,算法,java,开发语言)