Increment two array elements at a time so all equal the max value

Viewed 473

Given any array of natural numbers, for eg: [2, 1, 2, 3] Find if array can be converted into Max array (print- "YES") or if not (print - "NO")

to make it Max array - convert every element of array equal to its maximum element. In above eg it will be [3, 3, 3, 3] but by following these rules -

  1. Increment any two elements by 1 at a time (exactly 2 elements. you cannot increment one or more than two elements at a time)
  2. Do this multiple times until you convert every element equal to max elements (print "YES" if possible else "NO")

Sample input: [2, 1, 2, 3]

Expected Output: "YES"

Explanation:

Step 1: increment first and second element by 1 -

[3, 2, 2, 3]

Step 2: increment second and third element by 1 -

[3, 3, 3, 3]

Can anyone point to the solution - any link, similar question, pattern or solution? Thank you

Edit:

I tried this approach to solve it -

  1. Find max value and remove it
  2. Find duplicate pair of each number and after that for remaining single numbers
  • there should be equal number of even and odd numbers

But can't quite get the correct result.

2 Answers

This is actually a known interview/programming contest question, but it's usually presented as "Given an array of positive integers, can you reduce them all to zero, two (or k) at a time?"

There is a simple solution: we only need to check whether we can reach the desired sum in steps of two (i.e. check parity), and whether the smallest number can reach the maximum by the time all other numbers have reached the maximum.

def is_possible(nums: List[int]) -> bool:
    smallest, largest = min(nums), max(nums)
    total_needed = sum(largest - x for x in nums)
    if total_needed % 2 == 1:
        return False
    return 2 * (largest - smallest) <= total_needed

this gives:

assert is_possible([6, 6, 10])    == True
assert is_possible([2, 1, 2, 3])  == True
assert is_possible([1, 5, 5, 9])  == True
assert is_possible([1, 2, 9])     == False
assert is_possible([1, 4, 9, 10]) == False
assert is_possible([1, 6, 6, 9])  == False

A more specific problem statement

One unfortunate feature of this problem is that despite the intuitively simple solution, a full proof of this solution is rather long. The original statement of the problem has caused confusion over the meaning of the phrase 'max array', so I'll try to give a precise mathematical description of the problem, and then transform that. This will then explain why the code is implementing the natural 'greedy strategy' for the problem, and why that works.

Original Problem: Given a zero-indexed array of positive integers A of length n > 1, you are allowed to perform the following operation any number of times: Choose two distinct indices i, j with 0 <= i < j < n, such that A[i] < max(A) and A[j] < max(A), and increment A[i] and A[j]. Determine whether you can make all of the array elements equal.

The greedy strategy

The 'greedy' or brute-force solution to this problem, if performance wasn't a concern, would be to select the two smallest elements from A and increment them, repeating this until either all or all but one element from A was equal to max(A). If exactly one element isn't equal to max(A), we failed and the task is impossible (this statement requires a proof); otherwise it is clearly possible.

def is_possible_brute_force(nums: List[int]) -> bool:
    largest = max(nums)
    nums.sort()

    while nums[0] != largest:
        first = nums.pop(0)
        second = nums.pop(0)
        if second == largest and first != largest:  # If exactly one number not max
            return False
        bisect.insort(nums, first+1)
        bisect.insort(nums, second+1)

    return all(x == largest for x in nums)  # Always true

Our goal is to simulate the result of this procedure, without actually doing it. We can observe immediately that the task is impossible if the sum of gaps between elements of A and max(A), which we might call total_needed, is odd. It's also true that we can apply the following transformation to the problem without changing the answer:

New Problem: Let M = max(A). Let B be A after the transform A[i] -> M - A[i]. Our allowed operation is now to decrement two distinct indices of B, and our goal is to reach the zero array.

It's easier to think in terms of B and decrements. The first strategy you might think of is: repeatedly decrement the two largest elements of B, i.e. the greedy strategy. This strategy turns out to be optimal, finding a solution whenever it exists.

Let Max_B = max(B) and let Sum_B = sum(B). Since we know that no solution exists if Sum_B is odd, we can assume that Sum_B is even from here on. There are two possibilities:

  1. Max_B > Sum_B - Max_B. In this case, no matter what we do, after performing Sum_B - Max_B decrements, all elements except Max_B are zero, so no solution is possible.
  2. Max_B <= Sum_B - Max_B. In this case, a solution is always possible.

To prove (2), it suffices to prove two things: i. If Max_B <= Sum_B - Max_B, then after decrementing the two largest elements, we still have Max_B <= Sum_B - Max_B for our new array. ii. The only configuration where no moves are possible yet B is nonzero is when exactly one element of B is nonzero; in this case, Max_B > Sum_B - Max_B

The proof of the first statement is algebraic manipulation and case analysis that is fairly unsurprising, so I'll omit that from this already lengthy proof. The first Python code snippet can now be understood as checking the parity of total_needed, and whether we are in situation (1) or (2) above.

Edit: The original posted version of the code had a mistake in the final line, using an incorrect variable name and a flipped inequality sign, compared to the equation in the explanation and proof. Credit and thanks goes to user Breaking Not So Bad for catching this.

Here is a simple algorithm that should work. The idea is to increment the lowest values first:

  1. Find the maximum value. Let's call it max.

  2. Find the minimum value. Let's call it min. If min = max, output YES.

  3. Find an element with value min and increment it.

  4. Find the minimum value of the other elements. Let's call it min. If min = max, output NO.

  5. Find an element (other than the previous one) with value min and increment it.

  6. Go to step 2.

Related