Minimum Moves to Make Array Complementary
MedPrompt
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.length2 <= n <= 1051 <= nums[i] <= limit <= 105nis even.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves checking every possible target sum for the pairs (nums[i], nums[n-1-i]). The possible sums range from 2 (by changing both numbers to 1) to 2 * limit (by changing both to limit). For each potential target sum, we calculate the total number of moves required to make all pairs sum up to it. We keep track of the minimum moves found across all target sums.
Algorithm
- Initialize
minMovesto a very large value, for instance,n(the maximum possible moves). - Iterate through every possible
targetSumfrom2to2 * limit. - For each
targetSum, initialize a countercurrentMovesto0. - Iterate through the first half of the array, considering pairs
(nums[i], nums[n - 1 - i]). - For each pair
(a, b):- If
a + b == targetSum, the cost is 0 moves. - If
a + b != targetSumbut thetargetSumcan be achieved with one change (i.e.,targetSumis in the range[1 + min(a, b), limit + max(a, b)]), add 1 tocurrentMoves. - Otherwise, two changes are necessary, so add 2 to
currentMoves.
- If
- After checking all pairs,
currentMovesholds the total moves for the currenttargetSum. UpdateminMoves = min(minMoves, currentMoves). - After iterating through all possible
targetSumvalues,minMoveswill hold the minimum moves required.
Walkthrough
The core idea is to find a targetSum that minimizes the total moves. The targetSum can range from 2 to 2 * limit.
We iterate through each possible targetSum in this range. For each targetSum, we then iterate through all n/2 pairs of elements (a, b) where a = nums[i] and b = nums[n - 1 - i]. For each pair, we calculate the moves needed to make their sum equal to targetSum:
- 0 moves: if
a + bis already equal totargetSum. - 1 move: if
a + b != targetSum, but we can change just one of the numbers to achieve thetargetSum. This is possible if thetargetSumis within the range[1 + min(a, b), limit + max(a, b)]. - 2 moves: if we must change both numbers. This is the case for any
targetSumthat cannot be achieved with 0 or 1 move.
We sum the moves for all pairs to get the total moves for the current targetSum. The minimum of these totals over all possible targetSums is the answer.
class Solution { public int minMoves(int[] nums, int limit) { int n = nums.length; int minMoves = n; // Maximum possible moves for (int targetSum = 2; targetSum <= 2 * limit; targetSum++) { int currentMoves = 0; for (int i = 0; i < n / 2; i++) { int a = nums[i]; int b = nums[n - 1 - i]; if (a + b == targetSum) { // 0 moves needed continue; } int minVal = Math.min(a, b); int maxVal = Math.max(a, b); if (targetSum >= 1 + minVal && targetSum <= limit + maxVal) { currentMoves += 1; } else { currentMoves += 2; } } minMoves = Math.min(minMoves, currentMoves); } return minMoves; }}Complexity
Time
O(n * limit). The outer loop runs `2 * limit` times, and for each iteration, the inner loop runs `n/2` times.
Space
O(1), as we only use a few variables to store counts and intermediate values.
Trade-offs
Pros
Simple to understand and implement.
Requires minimal space.
Cons
The time complexity is very high, making it impractical for the given constraints.
It will result in a 'Time Limit Exceeded' error on most online judges for this problem.
This approach improves upon the brute-force method by avoiding recalculating the total moves for each target sum from scratch. It uses a difference array, a technique related to sweep-line algorithms, to efficiently calculate the moves for all possible target sums simultaneously. We analyze how the number of moves changes as the target sum increases and use an array to record these changes at specific 'event points'. By processing these changes in a single pass, we can find the optimal target sum efficiently.
Algorithm
- Create a difference array
deltaof size2 * limit + 2, initialized to all zeros. This array will store the change in the number of moves as the target sum increases. - Iterate through the
n/2pairs(a, b)from the input array. - For each pair, determine the move savings and update the
deltaarray:- A 1-move cost is achievable for
targetSumin[1 + min(a, b), limit + max(a, b)]. This is a reduction of 1 move from the default 2 moves. We record this by decrementingdeltaat the start of the range and incrementing it after the end:delta[1 + min(a, b)]--anddelta[limit + max(a, b) + 1]++. - A 0-move cost is achievable for
targetSum = a + b. This is a further reduction of 1 move. We record this bydelta[a + b]--anddelta[a + b + 1]++.
- A 1-move cost is achievable for
- After populating the
deltaarray, calculate the actual moves for eachtargetSum. - Initialize
currentMoveston(the baseline cost of 2 moves for each of then/2pairs) andminMoveston. - Iterate from
targetSum = 2to2 * limit. In each step, updatecurrentMovesby addingdelta[targetSum]. This gives the total moves for the currenttargetSum. - Update
minMoves = min(minMoves, currentMoves)in each step. - Return
minMoves.
Walkthrough
The total number of moves for any targetSum can be calculated by starting with a baseline and applying adjustments. The baseline cost is 2 moves for each of the n/2 pairs, totaling n moves. We can then find how many moves can be saved for each targetSum.
For a pair (a, b), we can save moves depending on the targetSum:
- Save 1 move (cost becomes 1): If we only need to change one number. This is possible for any
targetSumin the range[1 + min(a, b), limit + max(a, b)]. - Save 2 moves (cost becomes 0): If
a + bis already equal to thetargetSum. This is an additional saving of 1 move on top of the previous case.
We can model these savings as updates over ranges of targetSums. A difference array, let's call it delta, is perfect for this. delta[T] will store the change in the total number of moves as we go from targetSum = T-1 to T. After populating delta by processing all pairs, we can find the actual cost for each targetSum by computing the prefix sum of delta and adding it to the baseline cost of n.
class Solution { public int minMoves(int[] nums, int limit) { int n = nums.length; // delta[i] stores the change in moves when the target sum changes from i-1 to i. int[] delta = new int[2 * limit + 2]; for (int i = 0; i < n / 2; i++) { int a = nums[i]; int b = nums[n - 1 - i]; int minVal = Math.min(a, b); int maxVal = Math.max(a, b); int sum = a + b; // For any target sum, we assume 2 moves are needed initially. // The total moves start at n (2 moves for each of n/2 pairs). // Range where 1 move is sufficient: [1 + minVal, limit + maxVal] // For sums in this range, we save 1 move (cost changes from 2 to 1). // We apply a decrement over this range using the difference array. delta[1 + minVal]--; delta[limit + maxVal + 1]++; // For the specific sum `a + b`, 0 moves are needed. // This saves another move (cost changes from 1 to 0). // We apply another decrement for this single sum. delta[sum]--; delta[sum + 1]++; } // Calculate the actual moves for each possible target sum by taking a prefix sum. int currentMoves = n; // Start with the baseline of 2 moves per pair. int minMoves = n; for (int targetSum = 2; targetSum <= 2 * limit; targetSum++) { currentMoves += delta[targetSum]; minMoves = Math.min(minMoves, currentMoves); } return minMoves; }}Complexity
Time
O(n + limit). We iterate through `n/2` pairs once to populate the `delta` array (`O(n)`), and then iterate through the `delta` array of size `O(limit)` once to find the minimum moves (`O(limit)`).
Space
O(limit), as we need a `delta` array of size `2 * limit + 2`.
Trade-offs
Pros
Highly efficient and optimal for the given constraints.
Solves the problem within the time limits.
Cons
Requires extra space proportional to
limit.The logic is more complex to understand and implement compared to the brute-force approach.
Solutions
Solution
class Solution {public int minMoves(int[] nums, int limit) { int n = nums.length; int[] d = new int[limit * 2 + 2]; for (int i = 0; i < n >> 1; ++i) { int a = Math.min(nums[i], nums[n - i - 1]); int b = Math.max(nums[i], nums[n - i - 1]); d[2] += 2; d[limit * 2 + 1] -= 2; d[a + 1] -= 1; d[b + limit + 1] += 1; d[a + b] -= 1; d[a + b + 1] += 1; } int ans = n, s = 0; for (int i = 2; i <= limit * 2; ++i) { s += d[i]; if (ans > s) { ans = s; } } return ans; }}class Solution {public: int minMoves(vector<int> &nums, int limit) { int n = nums.size(); vector<int> d(limit * 2 + 2); for (int i = 0; i < n >> 1; ++i) { int a = min(nums[i], nums[n - i - 1]); int b = max(nums[i], nums[n - i - 1]); d[2] += 2; d[limit * 2 + 1] -= 2; d[a + 1] -= 1; d[b + limit + 1] += 1; d[a + b] -= 1; d[a + b + 1] += 1; } int ans = n, s = 0; for (int i = 2; i <= limit * 2; ++i) { s += d[i]; if (ans > s) { ans = s; } } return ans; }};class Solution: def minMoves(self, nums: List[int], limit: int) -> int: d = [0] * (limit * 2 + 2) n = len(nums) for i in range(n >> 1): a, b = min(nums[i], nums[n - i - 1]), max(nums[i], nums[n - i - 1]) d[2] += 2 d[limit * 2 + 1] -= 2 d[a + 1] -= 1 d[b + limit + 1] += 1 d[a + b] -= 1 d[a + b + 1] += 1 ans, s = n, 0 for v in d[2: limit * 2 + 1]: s += v if ans > s: ans = s return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.