1354. Construct Target Array With Multiple Sums
Problem Description
You start with an array arr
containing n
ones: [1, 1, 1, ..., 1]
. Your goal is to transform this array into a given target
array of n
integers.
The transformation follows these rules:
- Calculate
x
as the sum of all elements currently in your array - Choose any index
i
(where0 <= i < n
) and replace the value at that index withx
- Repeat this process as many times as needed
You need to determine if it's possible to transform the initial array of ones into the target
array using these operations. Return true
if the transformation is possible, otherwise return false
.
For example, if you start with [1, 1, 1]
:
- Sum is 3, you could replace index 0 to get
[3, 1, 1]
- Sum is 5, you could replace index 1 to get
[3, 5, 1]
- Sum is 9, you could replace index 2 to get
[3, 5, 9]
The solution works backwards from the target
array. Since each operation replaces an element with the sum of all elements, the largest element in the current array must have been the most recently modified. By reversing this process, we can check if we can reach the initial array of all ones. The algorithm uses a max heap to efficiently track the largest element and simulates the reverse operations until either we reach all ones (return true
) or determine it's impossible (return false
).
Intuition
The key insight is that working forward from the initial array [1, 1, 1, ...]
to the target
array is difficult because at each step, we have multiple choices for which index to update, and it's unclear which choice leads to the target.
However, if we think about the problem in reverse, starting from the target
array and working backwards to [1, 1, 1, ...]
, the problem becomes much clearer. Why? Because in any array state, the largest element must have been the most recently created element. This is because when we replace an element with the sum of all elements, the new value is always larger than any individual element that existed before.
Consider how an element becomes the largest: if we have an array and perform the operation, the element we just updated becomes the sum of all elements, which is guaranteed to be the largest. So when looking at any array state, we can confidently say the largest element was produced in the most recent operation.
Working backwards, if the current largest element is mx
and it was created by summing all elements, then before this operation:
- The element at this position had some smaller value
- The sum of all other elements was
t = current_sum - mx
- The original value at this position was
mx - t
We can repeatedly "undo" operations by:
- Finding the largest element
mx
- Computing what it was before:
mx - t
wheret
is the sum of all other elements - Replacing
mx
with this previous value
If at any point we get invalid values (negative or zero), we know the transformation is impossible. We continue this reverse process until all elements become 1
, indicating we've successfully traced back to the initial state.
The modulo operation mx % t
in the solution is an optimization - instead of repeatedly subtracting t
from mx
until we get a valid value, we can directly compute the remainder, which gives us the value before multiple operations were applied consecutively to the same index.
Learn more about Heap (Priority Queue) patterns.
Solution Approach
The solution implements the reverse construction approach using a max heap (priority queue) to efficiently track and extract the largest element at each step.
Data Structures Used:
- Max Heap: Python's
heapq
provides a min heap by default, so we negate all values to simulate a max heap - Variable
s
: Tracks the current sum of all elements in the array
Implementation Steps:
-
Initialization:
- Calculate the initial sum
s = sum(target)
- Create a max heap by negating all elements:
pq = [-x for x in target]
- Convert the list to a heap structure:
heapify(pq)
- Calculate the initial sum
-
Main Loop (
while -pq[0] > 1
):- Continue while the largest element is greater than 1
- Extract the largest element:
mx = -heappop(pq)
- Calculate the sum of remaining elements:
t = s - mx
-
Validation Checks:
- If
t == 0
: This means all other elements sum to 0, which is impossible since we started with positive integers - If
mx - t < 1
: The previous value would be less than 1, which violates our starting condition - Return
False
if either condition is met
- If
-
Calculate Previous Value:
- Instead of repeatedly subtracting
t
frommx
, use modulo:x = mx % t
- Special case: If
mx % t == 0
, setx = t
(this handles the case wheremx
is exactly divisible byt
) - This optimization handles cases where the same index was updated multiple times consecutively
- Instead of repeatedly subtracting
-
Update State:
- Push the calculated previous value back:
heappush(pq, -x)
- Update the sum:
s = s - mx + x
(removemx
, addx
)
- Push the calculated previous value back:
-
Termination:
- If the loop completes (all elements become 1), return
True
- The array has been successfully traced back to
[1, 1, 1, ...]
- If the loop completes (all elements become 1), return
Time Complexity: O(n log n)
for initial heapification, and each operation involves heap operations which are O(log n)
Space Complexity: O(n)
for storing the heap
The elegance of this solution lies in recognizing that the largest element uniquely identifies the last operation, allowing us to deterministically work backwards and verify if the transformation is possible.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the algorithm with target = [9, 3, 5]
.
Initial Setup:
- Start with array of ones:
[1, 1, 1]
- Can we reach
[9, 3, 5]
? Let's work backwards. - Sum
s = 9 + 3 + 5 = 17
- Max heap (negated):
[-9, -3, -5]
Step 1: Process largest element (9)
- Extract max:
mx = 9
- Sum of others:
t = s - mx = 17 - 9 = 8
- Previous value:
mx % t = 9 % 8 = 1
- Update array state:
[1, 3, 5]
- Update sum:
s = 17 - 9 + 1 = 9
- Heap becomes:
[-5, -3, -1]
Step 2: Process largest element (5)
- Extract max:
mx = 5
- Sum of others:
t = s - mx = 9 - 5 = 4
- Previous value:
mx % t = 5 % 4 = 1
- Update array state:
[1, 3, 1]
- Update sum:
s = 9 - 5 + 1 = 5
- Heap becomes:
[-3, -1, -1]
Step 3: Process largest element (3)
- Extract max:
mx = 3
- Sum of others:
t = s - mx = 5 - 3 = 2
- Previous value:
mx % t = 3 % 2 = 1
- Update array state:
[1, 1, 1]
- Update sum:
s = 5 - 3 + 1 = 3
- Heap becomes:
[-1, -1, -1]
Result: All elements are now 1, so we return true
.
Forward Verification:
Working forward from [1, 1, 1]
:
- Sum = 3, replace index 2:
[1, 1, 3]
- Sum = 5, replace index 1:
[1, 5, 3]
- Sum = 9, replace index 1:
[1, 9, 3]
- Sum = 13, replace index 0:
[13, 9, 3]
- Sum = 25, replace index 0:
[25, 9, 3]
- Continue... eventually reaching
[9, 3, 5]
✓
The backward approach efficiently validates that the transformation is possible without needing to explore all forward paths.
Solution Implementation
1from typing import List
2from heapq import heapify, heappop, heappush
3
4class Solution:
5 def isPossible(self, target: List[int]) -> bool:
6 """
7 Determine if we can transform an array of all 1s to the target array.
8 In each operation, we can replace one element with the sum of all elements.
9
10 Working backwards: the largest element must have been formed by summing all others.
11 So we reverse the operation by subtracting the sum of others from the largest.
12 """
13 # Calculate total sum of all elements
14 total_sum = sum(target)
15
16 # Create max heap (negate values since Python has min heap by default)
17 max_heap = [-num for num in target]
18 heapify(max_heap)
19
20 # Keep reducing the largest element until all elements become 1
21 while -max_heap[0] > 1:
22 # Get the current maximum element
23 current_max = -heappop(max_heap)
24
25 # Calculate sum of all other elements
26 sum_of_others = total_sum - current_max
27
28 # Edge cases where transformation is impossible
29 if sum_of_others == 0 or current_max - sum_of_others < 1:
30 return False
31
32 # Calculate the previous value before this element became current_max
33 # Use modulo for optimization when current_max >> sum_of_others
34 # If remainder is 0, the previous value was sum_of_others
35 previous_value = (current_max % sum_of_others) or sum_of_others
36
37 # Push the previous value back to heap
38 heappush(max_heap, -previous_value)
39
40 # Update total sum
41 total_sum = total_sum - current_max + previous_value
42
43 # All elements are now 1, transformation is possible
44 return True
45
1class Solution {
2 public boolean isPossible(int[] target) {
3 // Use max heap to always process the largest element
4 PriorityQueue<Long> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
5 long totalSum = 0;
6
7 // Initialize heap with all target values and calculate total sum
8 for (int value : target) {
9 totalSum += value;
10 maxHeap.offer((long) value);
11 }
12
13 // Work backwards from target array to see if we can reach all 1s
14 while (maxHeap.peek() > 1) {
15 // Get the largest element in current array
16 long maxElement = maxHeap.poll();
17
18 // Calculate sum of remaining elements
19 long remainingSum = totalSum - maxElement;
20
21 // Edge cases where transformation is impossible
22 // If remaining sum is 0 or if we can't form a positive value
23 if (remainingSum == 0 || maxElement - remainingSum < 1) {
24 return false;
25 }
26
27 // Calculate the value before the last operation(s)
28 // Using modulo to handle multiple operations at once
29 long previousValue = maxElement % remainingSum;
30
31 // If modulo is 0, the previous value should be remainingSum
32 if (previousValue == 0) {
33 previousValue = remainingSum;
34 }
35
36 // Add the calculated previous value back to heap
37 maxHeap.offer(previousValue);
38
39 // Update total sum by replacing maxElement with previousValue
40 totalSum = totalSum - maxElement + previousValue;
41 }
42
43 // If we reached here, all elements are 1
44 return true;
45 }
46}
47
1class Solution {
2public:
3 bool isPossible(vector<int>& target) {
4 // Use max heap to always get the largest element
5 priority_queue<int> maxHeap;
6 long long totalSum = 0;
7
8 // Initialize heap with all target values and calculate total sum
9 for (int i = 0; i < target.size(); i++) {
10 totalSum += target[i];
11 maxHeap.push(target[i]);
12 }
13
14 // Work backwards from target array to [1,1,...,1]
15 // In each step, we reverse the operation that created the largest element
16 while (maxHeap.top() != 1) {
17 int largestElement = maxHeap.top();
18 maxHeap.pop();
19
20 // Calculate sum of remaining elements (excluding the largest)
21 long long remainingSum = totalSum - largestElement;
22
23 // Check if backward operation is valid
24 // remainingSum must be at least 1, and the previous value must be positive
25 if (remainingSum < 1 || largestElement - remainingSum < 1) {
26 return false;
27 }
28
29 // Calculate the previous value before it became largestElement
30 // Previous value = largestElement - remainingSum (for single step)
31 // For multiple steps: use modulo to skip repeated subtractions
32 int previousValue = largestElement % remainingSum;
33
34 // Handle edge case: when previousValue becomes 0, it means
35 // largestElement is a multiple of remainingSum
36 // The actual previous value should be remainingSum in this case
37 if (previousValue == 0) {
38 previousValue = remainingSum;
39 }
40
41 // Add the calculated previous value back to heap
42 maxHeap.push(previousValue);
43
44 // Update total sum by replacing largestElement with previousValue
45 totalSum = totalSum - largestElement + previousValue;
46 }
47
48 // If we successfully reduced all elements to 1, return true
49 return true;
50 }
51};
52
1/**
2 * Determines if it's possible to transform an array of all 1s into the target array
3 * using the operation: replace any element with the sum of all elements
4 * @param target - The target array to check
5 * @returns true if transformation is possible, false otherwise
6 */
7function isPossible(target: number[]): boolean {
8 // Create a max priority queue to always process the largest element
9 const maxHeap = new MaxPriorityQueue<number>();
10
11 // Calculate the initial sum of all elements
12 let totalSum = 0;
13
14 // Add all elements to the max heap and calculate total sum
15 for (const element of target) {
16 totalSum += element;
17 maxHeap.enqueue(element);
18 }
19
20 // Work backwards from target array to array of all 1s
21 while (maxHeap.front() > 1) {
22 // Get the largest element from the heap
23 const largestElement = maxHeap.dequeue();
24
25 // Calculate the sum of all other elements
26 const sumOfOthers = totalSum - largestElement;
27
28 // Check if transformation is impossible
29 // sumOfOthers < 1: means other elements sum to less than 1
30 // largestElement - sumOfOthers < 1: means we can't reduce the largest element
31 if (sumOfOthers < 1 || largestElement - sumOfOthers < 1) {
32 return false;
33 }
34
35 // Calculate the previous value before transformation
36 // Use modulo to optimize multiple subtractions
37 // If largestElement % sumOfOthers is 0, use sumOfOthers instead
38 const previousValue = largestElement % sumOfOthers || sumOfOthers;
39
40 // Add the calculated previous value back to the heap
41 maxHeap.enqueue(previousValue);
42
43 // Update the total sum
44 totalSum = totalSum - largestElement + previousValue;
45 }
46
47 // If we successfully reduced all elements to 1, return true
48 return true;
49}
50
Time and Space Complexity
Time Complexity: O(n log n + k log n)
where n
is the length of the array target
and k
is the number of operations needed to reduce all elements to 1.
- Initial heapification takes
O(n)
time - Each iteration involves:
heappop()
:O(log n)
heappush()
:O(log n)
- The number of iterations
k
depends on the values in the target array. In the worst case, when we have large values that need to be reduced step by step,k
can be proportional tolog(max(target))
due to the modulo operation optimization - For practical purposes and simplified analysis, this is often expressed as
O(n log n)
when considering typical constraints
Space Complexity: O(n)
- The priority queue
pq
stores alln
elements from the target array, requiringO(n)
space - Additional variables (
s
,mx
,t
,x
) useO(1)
space - Total space complexity is
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow with Repeated Subtraction
The Pitfall:
A naive implementation might use a while loop to repeatedly subtract sum_of_others
from current_max
:
# WRONG - This causes Time Limit Exceeded! while current_max > sum_of_others: current_max -= sum_of_others previous_value = current_max
This approach fails catastrophically when current_max
is significantly larger than sum_of_others
. For example, with target = [1, 1000000000]
, you'd need nearly a billion iterations!
The Solution: Use the modulo operator for instant calculation:
# CORRECT - O(1) operation previous_value = current_max % sum_of_others if previous_value == 0: previous_value = sum_of_others
2. Incorrect Handling of Modulo Zero Case
The Pitfall:
When current_max % sum_of_others == 0
, using 0 as the previous value is incorrect:
# WRONG - This will push 0 to the heap previous_value = current_max % sum_of_others # Could be 0! heappush(max_heap, -previous_value)
This breaks the invariant that all values must be ≥ 1 since we started with an array of ones.
The Solution:
When the modulo result is 0, the previous value should be sum_of_others
:
# CORRECT - Handle the edge case previous_value = (current_max % sum_of_others) or sum_of_others
3. Missing Edge Case: Sum of Others is Zero
The Pitfall:
Forgetting to check if sum_of_others == 0
leads to division by zero error:
# WRONG - Division by zero when sum_of_others = 0 previous_value = current_max % sum_of_others # ZeroDivisionError!
The Solution: Always validate before the modulo operation:
# CORRECT - Check for zero first if sum_of_others == 0: return False previous_value = (current_max % sum_of_others) or sum_of_others
4. Not Checking if Previous Value Would Be Less Than 1
The Pitfall: Even after modulo optimization, forgetting to verify that the calculated previous value is valid:
# WRONG - Missing validation previous_value = (current_max % sum_of_others) or sum_of_others # What if current_max < sum_of_others + 1?
The Solution: Add a validation check before proceeding:
# CORRECT - Ensure we can reach a valid state if current_max - sum_of_others < 1: return False
5. Using Min Heap Instead of Max Heap
The Pitfall:
Python's heapq
is a min heap, so forgetting to negate values will process the smallest element instead of the largest:
# WRONG - This processes minimum elements max_heap = target.copy() heapify(max_heap) current_max = heappop(max_heap) # Actually gets minimum!
The Solution: Negate all values to simulate a max heap:
# CORRECT - Negate to create max heap behavior max_heap = [-num for num in target] heapify(max_heap) current_max = -heappop(max_heap) # Gets maximum
What's the relationship between a tree and a graph?
Recommended Readings
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!