Facebook Pixel

3344. Maximum Sized Array 🔒

MediumBit ManipulationBinary Search
Leetcode Link

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

  1. Precompute Possible Sums:
    Allocate an array f to store cumulative sums. The array is initialized with a size based on an estimated maximum n, specifically up to 1330. This allows the computation of potential sums without reevaluating them during each binary search step.

  2. Calculate Sums Using Bitwise Operations:
    For each possible i, iterate through possible j values up to i, and compute the sum using the formula 2 * (i | j) for each combination. Accumulate these values to build f which represents the cumulative sum for possible sizes of n.

    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

  1. Initialize Search Bounds:
    Set l (lower bound) to 1 and r (upper bound) to 1330. These bounds encompass all possible n values derived from the preprocessing step.

  2. Perform Binary Search:
    The goal is to find the largest n where f[n-1] * (n-1) * n / 2 <= s. Begin with the middle point m and compute whether the constraint is satisfied. Adjust l and r based on whether the condition holds, effectively narrowing down to the maximum feasible n.

    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 Evaluator

Example 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 array f to store cumulative sums for potential dimensions n from 1 to 1330. This requires calculating sums based on the formula A[i][j][k] = i * (j OR k).

    Consider n = 3:

    • For i = 0, 1, 2, compute sums using:
      • i = 1: 1 * (j OR k) when j, k < 3 gives combinations like 1*(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 array f.

Step 2: Binary Search

  • Initialize Search Bounds:
    Set l = 1 and r = 1330. We search for the largest n where the computed sum does not exceed s = 100.

  • Perform Binary Search:
    Check mid-point m of the current interval [l, r]. Calculate if f[m-1] * (m-1) * m / 2 <= 100:

    • For a smaller m, such as m = 3, check if the condition holds.
    • If it does, increase l to explore larger n; if not, decrease r.
  • Continue this process by selecting the midpoint and checking the feasible n until the largest valid n 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:

  1. Preprocessing Loop: This loop initializes the array f and has a nested loop inside it. The outer loop runs mx times and the inner loop runs up to i times for each i. 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)), where n corresponds to mx.

  2. Binary Search: This part involves searching within the bounds of 1 to mx, 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 size mx.

Therefore, the space complexity is (O(n)).

Learn more about how to find time and space complexity quickly.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

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

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More