100103. Divisible and Non-divisible Sums Difference

给你两个正整数 n 和 m 。

现定义两个整数 num1 和 num2 ,如下所示:

  • num1:范围 [1, n] 内所有 无法被 m 整除 的整数之和。
  • num2:范围 [1, n] 内所有 能够被 m 整除 的整数之和。

返回整数 num1 - num2 。

测试样例:

输入:n = 10, m = 3

输出:19

解释:
在这个示例中:

  • 范围 [1, 10] 内无法被 3 整除的整数为 [1,2,4,5,7,8,10] ,num1 = 这些整数之和 = 37 。
  • 范围 [1, 10] 内能够被 3 整除的整数为 [3,6,9] ,num2 = 这些整数之和 = 18 。

返回 37 - 18 = 19 作为答案。

解答:暴力遍历。

class Solution {
    public int differenceOfSums(int n, int m) {
        int n1 = 0, n2 = 0;
        for (int i = 1; i <= n; ++i) {
            if (i % m == 0) {
                n2 += i;
            } else {
                n1 += i;
            }
        }
        return n1 - n2;
    }
}

100085. Minimum Processing Time

你有 n 颗处理器,每颗处理器都有 4 个核心。现有 n * 4 个待执行任务,每个核心只执行 一个 任务。

给你一个下标从 0 开始的整数数组 processorTime ,表示每颗处理器最早空闲时间。另给你一个下标从 0 开始的整数数组 tasks ,表示执行每个任务所需的时间。返回所有任务都执行完毕需要的 最小时间 。

注意:每个核心独立执行任务。

测试样例:

输入:processorTime = [8,10], tasks = [2,2,3,1,8,7,4,5]

输出:16

解释:
最优的方案是将下标为 4, 5, 6, 7 的任务分配给第一颗处理器(最早空闲时间 time = 8),下标为 0, 1, 2, 3 的任务分配给第二颗处理器(最早空闲时间 time = 10)。

  • 第一颗处理器执行完所有任务需要花费的时间 = max(8 + 8, 8 + 7, 8 + 4, 8 + 5) = 16 。
  • 第二颗处理器执行完所有任务需要花费的时间 = max(10 + 2, 10 + 2, 10 + 3, 10 + 1) = 13 。

因此,可以证明执行完所有任务需要花费的最小时间是 16 。

解答:利用排序+贪婪。最早空闲的处理器,就需要处理耗时最长的任务。

class Solution {
    public int minProcessingTime(List<Integer> processorTime, List<Integer> tasks) {
        Collections.sort(tasks, (a, b) -> (b.compareTo(a)));
        Collections.sort(processorTime);
        Iterator<Integer> processIterator = processorTime.iterator();
        int count = 0, res = 0, cur = processIterator.next();
        for (int n : tasks) {
            res = Math.max(res, cur + n);
            ++count;
            if (count == 4) {
                count = 0;
                if (processIterator.hasNext()) {
                    cur = processIterator.next();
                }
            }
        }
        return res;
    }
}

8028. Apply Operations to Make Two Strings Equal

给你两个下标从 0 开始的二进制字符串 s1 和 s2 ,两个字符串的长度都是 n ,再给你一个正整数 x 。

你可以对字符串 s1 执行以下操作 任意次 :

  • 选择两个下标 i 和 j ,将 s1[i] 和 s1[j] 都反转,操作的代价为 x 。
  • 选择满足 i < n - 1 的下标 i ,反转 s1[i] 和 s1[i + 1] ,操作的代价为 1 。

请你返回使字符串 s1 和 s2 相等的 最小 操作代价之和,如果无法让二者相等,返回 -1 。

注意 ,反转字符的意思是将 0 变成 1 ,或者 1 变成 0 。

测试样例:

输入:s1 = "1100011000", s2 = "0101001010", x = 2

输出:4

解释:
我们可以执行以下操作:

  • 选择 i = 3 执行第二个操作。结果字符串是 s1 = "1101111000" 。
  • 选择 i = 4 执行第二个操作。结果字符串是 s1 = "1101001000" 。
  • 选择 i = 0 和 j = 8 ,执行第一个操作。结果字符串是 s1 = "0101001010" = s2 。

总代价是 1 + 1 + 2 = 4 。这是最小代价和。

解答:首先我们需要寻找到所有不相同的位。如果不相同的位数是奇数个,那么返回-1(所有操作只能使得2个不同位同时变化,奇数个无法完成)。然后我们需要利用动态规划,配合一点贪婪的思维。不同的数位,可以和最近的不同位利用操作2缓慢传播或者利用操作1,直接和更远的位变化(必然不会和更远的位利用1传播到达,一点贪婪的思维)。这样利用动态规划可以计算出最小开销。

class Solution {
    public int minOperations(String s1, String s2, int x) {
        if (!isValid(s1, s2)) {
            return -1;
        } else if (s1.equals(s2)) {
            return 0;
        }
        List<Integer> nonMatch = new ArrayList<>();
        for (int i = 0; i < s1.length(); ++i) {
            if (s1.charAt(i) != s2.charAt(i)) {
                nonMatch.add(i);
            }
        }

        int len = nonMatch.size();
        Integer[][] dp = new Integer[len + 1][2];
        dp[0][0] = 0;
        dp[1][1] = 0;
        for (int i = 1; i < nonMatch.size(); ++i) {
            dp[i + 1][0] = add(dp[i][1], x);
            dp[i + 1][1] = dp[i][0];
            int diff = Math.min(nonMatch.get(i) - nonMatch.get(i - 1), x);
            dp[i + 1][0] = min(add(dp[i - 1][0], diff), dp[i + 1][0]);
            dp[i + 1][1] = min(add(dp[i - 1][1], diff), dp[i + 1][1]);
        }
        return dp[len][0];
    }

    private boolean isValid(String s1, String s2) {
        int diff = 0;
        for (int i = 0; i < s1.length(); ++i) {
            diff += (s1.charAt(i) - '0');
            diff -= (s2.charAt(i) - '0');
        }
        return Math.abs(diff) % 2 == 0;
    }

    private Integer add(Integer ori, int diff) {
        if (ori == null) {
            return null;
        } else {
            return ori + diff;
        }
    }

    private Integer min(Integer ori, Integer other) {
        if (ori == null && other == null) {
            return null;
        } else if (ori == null) {
            return other;
        } else if (other == null) {
            return ori;
        } else {
            return Math.min(other, ori);
        }
    }
}

100087. Apply Operations on Array to Maximize Sum of Squares

给你一个下标从 0 开始的整数数组 nums 和一个 正 整数 k 。

你可以对数组执行以下操作 任意次 :

  • 选择两个互不相同的下标 i 和 j ,同时 将 nums[i] 更新为 (nums[i] AND nums[j]) 且将 nums[j] 更新为 (nums[i] OR nums[j]) ,OR 表示按位 或 运算,AND 表示按位 与 运算。

你需要从最终的数组里选择 k 个元素,并计算它们的 平方 之和。

请你返回你可以得到的 最大 平方和。

由于答案可能会很大,将答案对 10^9 + 7 取余 后返回。

测试样例

输入:nums = [2,6,5,8], k = 2

输出:261

解释:
我们可以对数组执行以下操作:

  • 选择 i = 0 和 j = 3 ,同时将 nums[0] 变为 (2 AND 8) = 0 且 nums[3] 变为 (2 OR 8) = 10 ,结果数组为 nums = [0,6,5,10] 。
  • 选择 i = 2 和 j = 3 ,同时将 nums[2] 变为 (5 AND 10) = 0 且 nums[3] 变为 (5 OR 10) = 15 ,结果数组为 nums = [0,6,0,15] 。

从最终数组里选择元素 15 和 6 ,平方和为 152 + 62 = 261 。261 是可以得到的最大结果。

解答:这道题目比较考验思维。假设c = a | b, d = a & b, 那么c + d = a + b。这样我们需要利用一下平方和性质,如果a + b = c + d,那么Math.max(c, d) - Math.min(c, d) > Math.max(a, b) - Math.min(a, b)时,(c,d)平方和更大。利用这个性质,我们需要把最大的k个数尽量放大。

class Solution {
    private static final int mod = 1_000_000_007;

    public int maxSum(List<Integer> nums, int k) {
        int[] arr = new int[nums.size()];
        int len = 0;
        for (int n : nums) {
            arr[len++] = n;
        }
        Arrays.sort(arr);
        long res = 0;
        Queue<Integer>[] queues = new Queue[30];
        for (int i = 0; i < 30; ++i) {
            queues[i] = new LinkedList<>();
        }
        for (int i = 0; i < len; ++i) {
            for (int j = 0; j < 30; ++j) {
                if (isOccupy(arr[i], j)) {
                    queues[j].add(i);
                }
            }
        }

        for (int i = len - 1; i >= len - k; --i) {
            for (int j = 0; j < 30; ++j) {
                if (!isOccupy(arr[i], j) && !queues[j].isEmpty() && queues[j].peek() < i) {
                    arr[i] += (1 << j);
                    int p = queues[j].poll();
                    arr[p] -= (1 << j);
                }
            }
            long tmp = arr[i];
            res = (res + tmp * tmp) % mod;
        }
        return (int) (res % mod);
    }

    private boolean isOccupy(int num, int offset) {
        int m = (num >> offset) & 1;
        return m == 1;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *