Skip to content

Commit af3d894

Browse files
committed
feat(algorithms, heap): construct target with sums
1 parent 3928d3f commit af3d894

19 files changed

+231
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Construct Target Array With Multiple Sums
2+
3+
You are given an array `target` of `n` integers. From a starting array `arr` consisting of `n` 1's, you may perform the
4+
following procedure :
5+
6+
- let x be the sum of all elements currently in your array.
7+
- choose index i, such that 0 <= i < n and set the value of arr at index i to x.
8+
- You may repeat this procedure as many times as needed.
9+
10+
Return true if it is possible to construct the target array from arr, otherwise, return false.
11+
12+
## Examples
13+
14+
Example 1:
15+
16+
```text
17+
Input: target = [9,3,5]
18+
Output: true
19+
Explanation: Start with arr = [1, 1, 1]
20+
[1, 1, 1], sum = 3 choose index 1
21+
[1, 3, 1], sum = 5 choose index 2
22+
[1, 3, 5], sum = 9 choose index 0
23+
[9, 3, 5] Done
24+
```
25+
26+
Example 2:
27+
28+
```text
29+
Input: target = [1,1,1,2]
30+
Output: false
31+
Explanation: Impossible to create target array from [1,1,1,1].
32+
```
33+
34+
Example 3:
35+
36+
```text
37+
Input: target = [8,5]
38+
Output: true
39+
```
40+
41+
## Constraints
42+
43+
- n == target.length
44+
- 1 <= n <= 5 * 10^4
45+
- 1 <= target[i] <= 10^9
46+
47+
## Topics
48+
49+
- Array
50+
- Heap (Priority Queue)
51+
52+
## Hints
53+
54+
- Given that the sum is strictly increasing, the largest element in the target must be formed in the last step by adding
55+
the total sum in the previous step. Thus, we can simulate the process in a reversed way.
56+
- Subtract the largest with the rest of the array, and put the new element into the array. Repeat until all elements
57+
become one
58+
59+
## Solution
60+
61+
The straightforward approach starts with an array of all 1’s and replaces one element with the sum of all elements,
62+
repeating this process. For example, starting with [1, 1, 1, 1], the sum is 4, and one number is replaced with this sum,
63+
leading to arrays like [4, 1, 1, 1], [1, 4, 1, 1], and so on. This process grows exponentially as the number of possible
64+
paths increases, making it computationally expensive for larger arrays. The expanding sum makes it hard to predict the
65+
correct path, and this approach struggles with large arrays due to its high computational cost and inefficiency.
66+
67+
The optimized approach works in reverse, starting from the target array and moving backward toward an array of all 1’s.
68+
This method reduces the number of possibilities by eliminating unnecessary branching. Instead of growing the array toward
69+
the target, the algorithm reduces the target by calculating the previous values. At each step, it identifies the largest
70+
number in the array and computes the sum of the remaining elements.
71+
72+
Instead of directly subtracting the largest number from the sum, the algorithm uses the modulo operation to find the
73+
previous value by computing the largest number modulo the sum of the other elements. This step-by-step reduction mimics
74+
working backward through the array, simplifying the array structure. A max heap ensures that the largest number is always
75+
processed first. This is crucial, as reducing the largest number drives the process toward convergence. The modulo
76+
operation progressively reduces the largest element, avoiding unnecessary branching and computations. The algorithm
77+
continues reducing until it reaches an array of all 1’s, confirming the target is achievable, or detects an invalid state,
78+
signaling failure. This optimized approach handles larger arrays effectively, reducing the largest number step-by-step and
79+
making the process computationally efficient and scalable. By working backward and minimizing the search space through
80+
subtraction and modulo, the algorithm ensures a unique path to the target, making it faster and more efficient.
81+
82+
The steps of the algorithm are as follows:
83+
84+
1. Initialize a max-heap and push all elements of the target array into the heap.
85+
2. Calculate the total_sum of the target array.
86+
3. Iterate while the max-heap is not empty:
87+
- Pop the largest element from the heap (current_max).
88+
- Compute the sum of the remaining elements: remaining_sum = total_sum - current_max.
89+
- Check for base cases:
90+
- If current_max == 1 or remaining_sum == 1, return True, because we can construct the array.
91+
- If remaining_sum == 0, or current_max < remaining_sum, or current_max % remaining_sum == 0, return False — it’s
92+
invalid or stuck in an infinite loop.
93+
- Simulate the reverse of the operation:
94+
- Compute the previous value before current_max was formed: updated_value = current_max % remaining_sum.
95+
- Update the total sum: total_sum = remaining_sum + updated_value.
96+
- Push updated_value back into the heap.
97+
98+
![Solution 1](images/solutions/construct_target_with_sums_solution_1.png)
99+
![Solution 2](images/solutions/construct_target_with_sums_solution_2.png)
100+
![Solution 3](images/solutions/construct_target_with_sums_solution_3.png)
101+
![Solution 4](images/solutions/construct_target_with_sums_solution_4.png)
102+
![Solution 5](images/solutions/construct_target_with_sums_solution_5.png)
103+
![Solution 6](images/solutions/construct_target_with_sums_solution_6.png)
104+
![Solution 7](images/solutions/construct_target_with_sums_solution_7.png)
105+
![Solution 8](images/solutions/construct_target_with_sums_solution_8.png)
106+
![Solution 9](images/solutions/construct_target_with_sums_solution_9.png)
107+
![Solution 10](images/solutions/construct_target_with_sums_solution_10.png)
108+
![Solution 11](images/solutions/construct_target_with_sums_solution_11.png)
109+
![Solution 12](images/solutions/construct_target_with_sums_solution_12.png)
110+
![Solution 13](images/solutions/construct_target_with_sums_solution_13.png)
111+
![Solution 14](images/solutions/construct_target_with_sums_solution_14.png)
112+
![Solution 15](images/solutions/construct_target_with_sums_solution_5.png)
113+
![Solution 16](images/solutions/construct_target_with_sums_solution_16.png)
114+
115+
### Time Complexity
116+
117+
The time complexity of the solution is O(nlog(n)) because each heap operation (push and pop) takes log(n) time and we
118+
may perform it up to O(n) times in the worst case.
119+
120+
### Space Complexity
121+
122+
The space complexity of the above solution is O(n), for storing the heap.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from typing import List
2+
import heapq
3+
4+
5+
def is_possible(target: List[int]) -> bool:
6+
if not target:
7+
return False
8+
9+
current_total_sum = sum(target)
10+
max_heap = []
11+
for x in target:
12+
heapq.heappush(max_heap, -x)
13+
14+
# While the largest element is greater than 1
15+
while -max_heap[0] > 1:
16+
# Pop the maximum element from the heap
17+
current_max = -heapq.heappop(max_heap)
18+
# Compute the sum of the remaining elements
19+
remaining_sum = current_total_sum - current_max
20+
21+
if remaining_sum == 1:
22+
return True
23+
24+
if remaining_sum == 0:
25+
return False
26+
27+
# Using modulo % to efficiently reverse multiple subtraction steps at once
28+
previous_max = current_max % remaining_sum
29+
30+
# If the result is invalid (≤ 0 or unchanged), return False.
31+
if previous_max == 0 or previous_max >= current_max:
32+
return False
33+
34+
heapq.heappush(max_heap, -previous_max)
35+
current_total_sum = remaining_sum + previous_max
36+
37+
return True
38+
39+
40+
def is_possible_2(target: List[int]) -> bool:
41+
# Calculate the total sum of the target array
42+
total_sum = sum(target)
43+
44+
# Convert the target array into a max heap by negating the values
45+
# (Python's heapq is a min-heap, so we negate to simulate a max-heap)
46+
max_heap = [-num for num in target]
47+
heapq.heapify(max_heap) # Turn the list into a heap
48+
49+
while True:
50+
# Pop the largest element (the most negative value, so negate it back)
51+
current_max = -heapq.heappop(max_heap)
52+
53+
# Calculate the sum of the remaining elements (total_sum - current_max)
54+
remaining_sum = total_sum - current_max
55+
56+
# Base cases:
57+
# If the current max is 1 or the sum of the remaining elements is 1, we can return True
58+
if current_max == 1 or remaining_sum == 1:
59+
return True
60+
61+
# Invalid cases:
62+
# If the sum of the remaining elements is 0, or the current max is smaller than
63+
# the remaining sum, or the current max divides evenly by the remaining sum, return False
64+
if (
65+
remaining_sum == 0
66+
or current_max < remaining_sum
67+
or current_max % remaining_sum == 0
68+
):
69+
return False
70+
71+
# Update the current_max with the modulo of remaining_sum to simulate the "reverse operation"
72+
updated_value = current_max % remaining_sum
73+
74+
# Update the total sum for the next iteration
75+
total_sum = remaining_sum + updated_value
76+
77+
# Push the updated value back into the heap (negate it to keep max-heap behavior)
78+
heapq.heappush(max_heap, -updated_value)
40 KB
Loading
90.7 KB
Loading
85.5 KB
Loading
89.8 KB
Loading
85.3 KB
Loading
88.1 KB
Loading
83.4 KB
Loading
87.1 KB
Loading

0 commit comments

Comments
 (0)