leetcode
leetcode 2701 ~ 2750
使数组互补的最少操作次数

使数组互补的最少操作次数

难度:

标签:

题目描述

You are given an integer array nums of even length n and an integer limit. In one move, you can replace any integer from nums with another integer between 1 and limit, inclusive.

The array nums is complementary if for all indices i (0-indexed), nums[i] + nums[n - 1 - i] equals the same number. For example, the array [1,2,3,4] is complementary because for all indices i, nums[i] + nums[n - 1 - i] = 5.

Return the minimum number of moves required to make nums complementary.

 

Example 1:

Input: nums = [1,2,4,3], limit = 4
Output: 1
Explanation: In 1 move, you can change nums to [1,2,2,3] (underlined elements are changed).
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
Therefore, nums[i] + nums[n-1-i] = 4 for every i, so nums is complementary.

Example 2:

Input: nums = [1,2,2,1], limit = 2
Output: 2
Explanation: In 2 moves, you can change nums to [2,2,2,2]. You cannot change any number to 3 since 3 > limit.

Example 3:

Input: nums = [1,2,1,2], limit = 2
Output: 0
Explanation: nums is already complementary.

 

Constraints:

  • n == nums.length
  • 2 <= n <= 105
  • 1 <= nums[i] <= limit <= 105
  • n is even.

代码结果

运行时间: 159 ms, 内存: 30.8 MB


/*
思路:
1. 使用 Java Stream 来进行统计和计算。
2. 对每一对 (nums[i], nums[n-1-i]),考虑它们的和。
3. 对每一个可能的和 nums[i] + nums[n-1-i],统计其频率。
4. 对于每一个目标和,从 2 到 2 * limit,计算出需要的最小操作次数。
*/

import java.util.stream.IntStream;

public class Solution {
    public int minMoves(int[] nums, int limit) {
        int n = nums.length;
        int[] delta = new int[2 * limit + 2];
        IntStream.range(0, n / 2).forEach(i -> {
            int a = nums[i], b = nums[n - 1 - i];
            delta[2] += 2;
            delta[Math.min(a, b) + 1] -= 1;
            delta[a + b] -= 1;
            delta[a + b + 1] += 1;
            delta[Math.max(a, b) + limit + 1] += 1;
        });
        return IntStream.range(2, 2 * limit + 1).reduce(n, (minMoves, i) -> {
            return Math.min(minMoves, delta[i] += (i == 2 ? 0 : delta[i - 1]));
        });
    }
}

解释

方法:

此题解利用了差分数组的技巧来优化区间更新的操作。差分数组diff用于记录操作的变化:初始时假设每对(i, n-1-i)需要两次操作来达到互补状态,这是diff数组全体加2的原因。对于每对a和b(其中a是较小的那个以简化区间操作),首先减去一次操作,因为他们已经共同贡献了一个和。然后再根据可能的最小和和最大和调整diff数组,以确保最少的修改次数覆盖更多的配对。最后,通过累积和找出最少操作次数,即差分数组的最小累积值。

时间复杂度:

O(n + limit)

空间复杂度:

O(limit)

代码细节讲解

🦆
在算法中,为什么初始时`diff[1]`增加2,这是基于什么考虑?
在算法中,初始时`diff[1]`增加2是基于假定每对元素(i, n-1-i)最初都需要两次操作来达到互补状态。这是考虑到在最坏的情况下,每对元素可能需要分别调整两次才能达到任何可能的目标和值。因此,算法开始时为每对元素预设了两次操作,然后再通过后续的差分操作来根据实际的元素值进行调整,以减少不必要的操作次数。
🦆
请解释为什么在`diff[a+b-1]`和`diff[a+b]`之间调整差分数组,这样的处理是如何影响最终的操作次数计算的?
在差分数组中,`diff[a+b-1]`和`diff[a+b]`之间的调整是为了有效地处理区间更新,这种调整方式是区间加法的常用技巧。通过在`diff[a+b-1]`处减1,我们开始减少从这点开始的操作次数,而在`diff[a+b]`处加1则是结束这个减少的区间。这意味着对于所有和值在a+b至b+limit之间的元素对,其所需的操作次数会被减少。这种差分处理使得累积和的计算可以快速地反映出各个和值所需的最终操作次数,从而找出最少操作次数。
🦆
算法在处理边界条件时,如何确保不会超出`diff`数组的有效索引范围?
算法在处理边界条件时,通过精心设计`diff`数组的大小和索引的调整来确保不会超出数组的有效范围。`diff`数组的长度被设置为`limit*2 + 1`,这是因为最大可能的和为`2*limit`(当两个最大元素相加时)。通过在计算差分时考虑元素值与`limit`的关系,并且在更新差分数组时始终检查索引是否合法(例如,确保在加上`limit`之后的索引仍然在数组范围内),算法避免了数组越界的风险。

相关问题