3344. Maximum Sized Array 🔒
Problem Description
The task is to determine the largest size for a three-dimensional array A
with dimensions n x n x n
such that the sum of all its elements does not exceed a given positive integer s
. The array is constructed with the elements A[i][j][k]
defined by the formula i * (j OR k)
, where 0 <= i, j, k < n
. The challenge is to find the maximum integer n
that allows the sum of all these elements in A
to stay within the limit of s
.
Intuition
To approach the solution, the problem can be tackled in two major parts: preprocessing and binary search. The preprocessing step involves precomputing possible sums that can be constructed from various sizes of n
. This is done by evaluating the expression f[n] = sum(i * (j OR k))
for all valid indices and storing these cumulative sums until a safe estimate of n
is reached.
By considering the mathematical behavior of the operations involved, especially the bitwise OR, we can deduce that the sum grows approximately as a fifth power of n
, i.e., (n-1)^5 / 4
. This approximation suggests that n
should not exceed approximately 1320 for feasible computation within most reasonable constraints of s
.
Once precomputation is complete, a binary search is employed to efficiently find the largest n
such that the cumulative sum for an (n-1)
-sized array is within the allowed limit s
. Binary searching between 1 and the estimated maximum ensures that the optimal n
is found quickly and accurately. The binary search leverages the cumulative sums precomputed in f
to decide whether increasing n
is safe with respect to the sum condition.
Learn more about Binary Search patterns.
Solution Approach
The solution employs a combination of preprocessing and binary search to determine the largest possible n
for which the sum of the 3D array A
does not exceed the given integer s
.
Preprocessing
-
Precompute Possible Sums:
Allocate an arrayf
to store cumulative sums. The array is initialized with a size based on an estimated maximumn
, specifically up to1330
. This allows the computation of potential sums without reevaluating them during each binary search step. -
Calculate Sums Using Bitwise Operations:
For each possiblei
, iterate through possiblej
values up toi
, and compute the sum using the formula2 * (i | j)
for each combination. Accumulate these values to buildf
which represents the cumulative sum for possible sizes ofn
.mx = 1330 f = [0] * mx for i in range(1, mx): f[i] = f[i - 1] + i for j in range(i): f[i] += 2 * (i | j)
Binary Search
-
Initialize Search Bounds:
Setl
(lower bound) to1
andr
(upper bound) to1330
. These bounds encompass all possiblen
values derived from the preprocessing step. -
Perform Binary Search:
The goal is to find the largestn
wheref[n-1] * (n-1) * n / 2 <= s
. Begin with the middle pointm
and compute whether the constraint is satisfied. Adjustl
andr
based on whether the condition holds, effectively narrowing down to the maximum feasiblen
.class Solution: def maxSizedArray(self, s: int) -> int: l, r = 1, mx while l < r: m = (l + r + 1) >> 1 if f[m - 1] * (m - 1) * m // 2 <= s: l = m else: r = m - 1 return l
Through these steps, the algorithm efficiently determines the largest dimension n
that satisfies the sum constraint, combining both pre-calculation and dynamic searching strategies.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
To understand the solution approach, let's take a small example where s = 100
. We want to find the largest n
for a 3D array A
such that the sum of all its elements does not exceed 100.
Step 1: Preprocessing
-
Precompute Possible Sums:
We create an arrayf
to store cumulative sums for potential dimensionsn
from1
to1330
. This requires calculating sums based on the formulaA[i][j][k] = i * (j OR k)
.Consider
n = 3
:- For
i = 0, 1, 2
, compute sums using:i = 1
:1 * (j OR k)
whenj, k < 3
gives combinations like1*(0 OR 0), 1*(0 OR 1), 1*(0 OR 2), 1*(1 OR 0), 1*(1 OR 1), 1*(1 OR 2), ...
, all computed to accumulate the sum.- Similar calculations follow for
i = 2
.
Using this approach, the cumulative sum for
n = 3
could be evaluated and stored in the arrayf
. - For
Step 2: Binary Search
-
Initialize Search Bounds:
Setl = 1
andr = 1330
. We search for the largestn
where the computed sum does not exceeds = 100
. -
Perform Binary Search:
Check mid-pointm
of the current interval[l, r]
. Calculate iff[m-1] * (m-1) * m / 2 <= 100
:- For a smaller
m
, such asm = 3
, check if the condition holds. - If it does, increase
l
to explore largern
; if not, decreaser
.
- For a smaller
-
Continue this process by selecting the midpoint and checking the feasible
n
until the largest validn
is determined.
Using this method, you identify the largest n
—let's say n = 2
—where all conditions are satisfied.
This walkthrough demonstrates how preprocessing coupled with a binary search can efficiently solve the problem of determining the largest possible dimension n
for the given sum condition s
.
Solution Implementation
1# Define the maximum index size
2MAX_INDEX = 1330
3
4# Initialize a list 'f' with zeros of size MAX_INDEX
5f = [0] * MAX_INDEX
6
7# Calculate values for each index in 'f'
8for i in range(1, MAX_INDEX):
9 # Update the current index by adding the previous value and the current index value
10 f[i] = f[i - 1] + i
11 for j in range(i):
12 # Add the result of 'i OR j' multiplied by 2 for each 'j' less than 'i'
13 f[i] += 2 * (i | j)
14
15class Solution:
16 def maxSizedArray(self, s: int) -> int:
17 # Initialize binary search boundaries
18 left, right = 1, MAX_INDEX
19 while left < right:
20 # Calculate the midpoint using right shift to divide by 2
21 mid = (left + right + 1) >> 1
22 # Check if the current setup can fit within the given size 's'
23 if f[mid - 1] * (mid - 1) * mid // 2 <= s:
24 # Move the left boundary up to continue searching for larger sizes
25 left = mid
26 else:
27 # Adjust the right boundary since the mid size is too large
28 right = mid - 1
29
30 # Return the largest possible valid size
31 return left
32
1class Solution {
2 // Define a constant for the upper limit of the array size
3 private static final int MAX_SIZE = 1330;
4
5 // Initialize an array to store precomputed cumulative sums
6 private static final long[] precomputedSums = new long[MAX_SIZE];
7
8 // Static block to precompute the values for the array
9 static {
10 // Loop through each element from 1 to MAX_SIZE - 1
11 for (int i = 1; i < MAX_SIZE; ++i) {
12 // Carry over the previous cumulative sum and add the current index
13 precomputedSums[i] = precomputedSums[i - 1] + i;
14 // Calculate and add the result of OR operations for all values from 0 to i-1
15 for (int j = 0; j < i; ++j) {
16 precomputedSums[i] += 2 * (i | j);
17 }
18 }
19 }
20
21 // Method to find the maximum size of an array whose sum does not exceed `s`
22 public int maxSizedArray(long s) {
23 int left = 1, right = MAX_SIZE;
24 while (left < right) {
25 // Compute mid-point by using integer division
26 int mid = (left + right + 1) >> 1;
27
28 // Determine if the calculated sum for this mid-point is within the allowed limit `s`
29 if (precomputedSums[mid - 1] * (mid - 1) * mid / 2 <= s) {
30 left = mid; // Move the left bound up to mid-point
31 } else {
32 right = mid - 1; // Move the right bound down, making mid-point too large
33 }
34 }
35 return left; // The maximum size of the array
36 }
37}
38
1#include <iostream>
2
3// Define a constant for the maximum value.
4const int MAXIMUM_INDEX = 1330;
5
6// Declare a global array 'f' to store precomputed values.
7long long precomputedValues[MAXIMUM_INDEX];
8
9// Lambda function to initialize the 'precomputedValues' array.
10auto initialize = [] {
11 precomputedValues[0] = 0; // Base case initialization.
12
13 // Populate the precomputedValues with calculated values.
14 for (int i = 1; i < MAXIMUM_INDEX; ++i) {
15 precomputedValues[i] = precomputedValues[i - 1] + i; // Accumulate the sum of indices.
16
17 // Iterate over all previous indices and update based on bitwise OR operation.
18 for (int j = 0; j < i; ++j) {
19 precomputedValues[i] += 2 * (i | j);
20 }
21 }
22
23 return 0; // Lambda must return an integer value.
24}(); // Execute the lambda to initialize the array.
25
26// Define a class that contains a public method to find the maximum sized array.
27class Solution {
28public:
29 // Method that determines the maximum size of a subset with specified conditions.
30 int maxSizedArray(long long sum) {
31 // Initialize binary search boundaries.
32 int left = 1, right = MAXIMUM_INDEX;
33
34 // Perform binary search to find the optimal size.
35 while (left < right) {
36 int middle = (left + right + 1) >> 1; // Calculate middle index.
37
38 // Check if the precomputed sum fits within the specified 'sum'.
39 if (precomputedValues[middle - 1] * (middle - 1) * middle / 2 <= sum) {
40 left = middle; // Update the left boundary if condition is satisfied.
41 } else {
42 right = middle - 1; // Otherwise, update the right boundary.
43 }
44 }
45
46 return left; // Return the calculated maximum size.
47 }
48};
49
1// Define a constant representing the maximum size of the array 'f' to be used.
2const MX = 1330;
3
4// Initialize an array 'f' with the size MX, and fill it with zero big integers.
5const f: bigint[] = Array(MX).fill(0n);
6
7// Immediately Invoked Function Expression (IIFE) to pre-calculate values for the array 'f'.
8(() => {
9 // Set the first element to 0 (though it's already initialized as 0).
10 f[0] = 0n;
11
12 // Loop from 1 to MX, filling the array 'f' with computed values.
13 for (let i = 1; i < MX; i++) {
14 // Calculate sum of integers up to i.
15 f[i] = f[i - 1] + BigInt(i);
16
17 // Additional loop to calculate the sum of 'i | j' for all j less than i.
18 for (let j = 0; j < i; j++) {
19 f[i] += BigInt(2) * BigInt(i | j);
20 }
21 }
22})();
23
24// Function to determine the maximum size of the array part that can sum up to 's'.
25function maxSizedArray(s: number): number {
26 // Initialize left and right bounds for binary search.
27 let l = 1; // Start from 1, as f[0] is trivially 0.
28 let r = MX;
29
30 // Convert the input number 's' to a big integer for comparison.
31 const target = BigInt(s);
32
33 // Use binary search to find the maximum size 'l' such that the condition holds.
34 while (l < r) {
35 // Compute the middle index using binary search logic.
36 const m = (l + r + 1) >> 1;
37
38 // Calculate the condition value using the precomputed array 'f'.
39 if ((f[m - 1] * BigInt(m - 1) * BigInt(m)) / BigInt(2) <= target) {
40 // Move the left bound up if the condition is fulfilled.
41 l = m;
42 } else {
43 // Otherwise, adjust the right bound.
44 r = m - 1;
45 }
46 }
47
48 // Return the maximum size found fulfilling the condition.
49 return l;
50}
51
Time and Space Complexity
The time complexity of the code involves two main parts:
-
Preprocessing Loop: This loop initializes the array
f
and has a nested loop inside it. The outer loop runsmx
times and the inner loop runs up toi
times for eachi
. This results in a total complexity of [ \sum_{i=1}^{mx-1} i = \frac{(mx-1) \times mx}{2} ] Hence, the preprocessing has a time complexity of (O(n^2)), wheren
corresponds tomx
. -
Binary Search: This part involves searching within the bounds of
1
tomx
, making the time complexity of the binary search (O(\log n)).
Therefore, the total time complexity of the algorithm is (O(n^2 + \log n)).
For space complexity:
- The space used is mainly for the array
f
, which is of sizemx
.
Therefore, the space complexity is (O(n)).
Learn more about how to find time and space complexity quickly.
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!