Facebook Pixel

1183. Maximum Number of Ones πŸ”’

Problem Description

You have a matrix M with dimensions width Γ— height. Every cell in this matrix can only contain either 0 or 1. There's a special constraint: any square sub-matrix of size sideLength Γ— sideLength within M can have at most maxOnes ones.

Your task is to find the maximum possible number of ones that can be placed in the entire matrix M while respecting this constraint.

For example, if you have a 3Γ—3 matrix with sideLength = 2 and maxOnes = 1, then every 2Γ—2 square region in the matrix can contain at most 1 one. You need to strategically place ones to maximize the total count while ensuring no 2Γ—2 square violates this rule.

The key insight is that when you place a 1 at position (i, j), it affects all square sub-matrices that include this position. The solution leverages the fact that positions at coordinates (i mod sideLength, j mod sideLength) are "equivalent" - they appear in the same relative position within their respective square regions. By counting how many times each equivalent position appears across the entire matrix and selecting the maxOnes most frequent positions, we can maximize the total number of ones.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Think about overlaying a grid of sideLength Γ— sideLength squares across the entire matrix. Each square can have at most maxOnes ones. The key observation is that these squares repeat in a pattern - they tile the matrix.

Imagine we have a single sideLength Γ— sideLength template square. When we tile this template across the matrix, position (0, 0) in the template appears at positions (0, 0), (0, sideLength), (0, 2Γ—sideLength), ... and also at (sideLength, 0), (2Γ—sideLength, 0), etc. in the actual matrix.

Each position (i, j) in the matrix belongs to exactly one position in this template square, specifically position (i % sideLength, j % sideLength). This means that if we decide to place a 1 at position (r, c) in the template (where r < sideLength and c < sideLength), then ALL positions (i, j) in the matrix where i % sideLength = r and j % sideLength = c must also be 1.

Since we can only choose maxOnes positions in the template to set as 1, we want to choose the positions that appear most frequently when the template is tiled across the entire matrix. A position (r, c) in the template appears exactly ⌈width/sideLengthβŒ‰ Γ— ⌈height/sideLengthβŒ‰ times (accounting for partial tiles at the edges).

By counting how many times each template position appears in the full matrix and selecting the maxOnes positions with the highest counts, we maximize the total number of ones in the matrix while respecting the constraint that each sideLength Γ— sideLength square has at most maxOnes ones.

Learn more about Greedy, Math, Sorting and Heap (Priority Queue) patterns.

Solution Approach

The implementation follows the pattern-based counting strategy identified in our intuition. Let's denote x = sideLength for simplicity.

We create an array cnt of size x * x to represent all possible positions in our template square. Each index in this array corresponds to a unique position (i % x, j % x) in the template.

For each cell (i, j) in the matrix:

  1. Calculate its corresponding position in the template: (i % x, j % x)
  2. Convert this 2D position to a 1D index: k = (i % x) * x + (j % x)
  3. Increment the counter at cnt[k] by 1

This counting process tells us how many times each template position appears when tiled across the entire width Γ— height matrix.

After counting all positions:

  1. Sort the cnt array in descending order to prioritize positions that appear most frequently
  2. Select the top maxOnes positions (these are the positions we'll set to 1 in our template)
  3. Return the sum of counts for these selected positions

The key insight is that by selecting the maxOnes most frequent positions, we maximize the total number of ones. Since each selected template position forces all its corresponding positions in the matrix to be 1, choosing the positions with highest frequency gives us the maximum possible ones.

Time complexity: O(width * height + xΒ²log(xΒ²)) where the first term is for counting and the second is for sorting. Space complexity: O(xΒ²) for the counting array.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's work through a concrete example with a 5Γ—4 matrix where sideLength = 2 and maxOnes = 1.

Step 1: Understanding the Template We have a 2Γ—2 template square with positions:

Template positions:
(0,0) (0,1)
(1,0) (1,1)

Step 2: Mapping Matrix Positions to Template Each position in our 5Γ—4 matrix maps to a template position based on (i % 2, j % 2):

Matrix (5Γ—4):         Template Position:
[0,0] [0,1] [0,2] [0,3]    (0,0) (0,1) (0,0) (0,1)
[1,0] [1,1] [1,2] [1,3]    (1,0) (1,1) (1,0) (1,1)
[2,0] [2,1] [2,2] [2,3]    (0,0) (0,1) (0,0) (0,1)
[3,0] [3,1] [3,2] [3,3]    (1,0) (1,1) (1,0) (1,1)
[4,0] [4,1] [4,2] [4,3]    (0,0) (0,1) (0,0) (0,1)

Step 3: Count Frequencies Now we count how many times each template position appears:

  • Position (0,0): appears at matrix positions [0,0], [0,2], [2,0], [2,2], [4,0], [4,2] β†’ 6 times
  • Position (0,1): appears at matrix positions [0,1], [0,3], [2,1], [2,3], [4,1], [4,3] β†’ 6 times
  • Position (1,0): appears at matrix positions [1,0], [1,2], [3,0], [3,2] β†’ 4 times
  • Position (1,1): appears at matrix positions [1,1], [1,3], [3,1], [3,3] β†’ 4 times

Converting to 1D array indices (using index = row * 2 + col):

  • cnt[0] = 6 (for template position (0,0))
  • cnt[1] = 6 (for template position (0,1))
  • cnt[2] = 4 (for template position (1,0))
  • cnt[3] = 4 (for template position (1,1))

Step 4: Select Top maxOnes Positions Sort counts in descending order: [6, 6, 4, 4]

Since maxOnes = 1, we select only 1 position. We can choose either position with count 6 (template positions (0,0) or (0,1)).

Step 5: Calculate Result Maximum ones possible = 6

Verification: If we choose template position (0,0), our matrix would look like:

[1] [0] [1] [0]
[0] [0] [0] [0]
[1] [0] [1] [0]
[0] [0] [0] [0]
[1] [0] [1] [0]

Every 2Γ—2 square contains exactly 1 one:

  • Square at [0,0]: contains [0,0]=1, others=0 βœ“
  • Square at [0,1]: contains [0,2]=1, others=0 βœ“
  • Square at [1,0]: contains [2,0]=1, others=0 βœ“
  • And so on...

The constraint is satisfied, and we've placed the maximum possible 6 ones.

Solution Implementation

1class Solution:
2    def maximumNumberOfOnes(
3        self, width: int, height: int, sideLength: int, maxOnes: int
4    ) -> int:
5        """
6        Calculate the maximum number of 1s that can be placed in a width x height matrix
7        such that each sideLength x sideLength submatrix has at most maxOnes 1s.
8      
9        Strategy: The matrix can be tiled by sideLength x sideLength submatrices.
10        Each position in the submatrix pattern appears multiple times across the full matrix.
11        We count how many times each position appears, then greedily select the most frequent positions.
12      
13        Args:
14            width: Width of the matrix
15            height: Height of the matrix
16            sideLength: Side length of each square submatrix
17            maxOnes: Maximum number of 1s allowed in each submatrix
18          
19        Returns:
20            Maximum total number of 1s that can be placed in the matrix
21        """
22        # Store frequency count for each position in the sideLength x sideLength pattern
23        position_frequencies = [0] * (sideLength * sideLength)
24      
25        # Count how many times each position in the pattern appears in the full matrix
26        for row in range(width):
27            for col in range(height):
28                # Map the current position to its corresponding position in the pattern
29                # Using row-major indexing: position = (row_in_pattern * sideLength + col_in_pattern)
30                pattern_position = (row % sideLength) * sideLength + (col % sideLength)
31                position_frequencies[pattern_position] += 1
32      
33        # Sort positions by frequency in descending order to maximize the result
34        position_frequencies.sort(reverse=True)
35      
36        # Select the top maxOnes positions with highest frequencies
37        # and sum their frequencies to get the maximum number of 1s
38        return sum(position_frequencies[:maxOnes])
39
1class Solution {
2    public int maximumNumberOfOnes(int width, int height, int sideLength, int maxOnes) {
3        // Create an array to count frequency of each position within a sideLength x sideLength tile
4        int tileSize = sideLength;
5        int[] positionFrequency = new int[tileSize * tileSize];
6      
7        // Count how many times each position in the tile appears across the entire matrix
8        for (int row = 0; row < height; row++) {
9            for (int col = 0; col < width; col++) {
10                // Map the current position to its corresponding position within the tile
11                // Using modulo to find the relative position within the repeating tile pattern
12                int tilePosition = (row % tileSize) * tileSize + (col % tileSize);
13                positionFrequency[tilePosition]++;
14            }
15        }
16      
17        // Sort the frequency array to identify positions that appear most frequently
18        Arrays.sort(positionFrequency);
19      
20        // Calculate the maximum number of ones by selecting the most frequent positions
21        int totalOnes = 0;
22        for (int i = 0; i < maxOnes; i++) {
23            // Add frequencies starting from the highest (at the end of sorted array)
24            totalOnes += positionFrequency[positionFrequency.length - 1 - i];
25        }
26      
27        return totalOnes;
28    }
29}
30
1class Solution {
2public:
3    int maximumNumberOfOnes(int width, int height, int sideLength, int maxOnes) {
4        // The key insight: the grid can be tiled by sideLength x sideLength squares
5        // Each position (i, j) maps to position (i % sideLength, j % sideLength) in the base tile
6        // We need to find which positions in the base tile appear most frequently across the entire grid
7      
8        // Create a frequency array for each position in the base tile
9        // Total positions in base tile = sideLength * sideLength
10        vector<int> frequency(sideLength * sideLength, 0);
11      
12        // Count how many times each position in the base tile appears in the full grid
13        for (int row = 0; row < height; ++row) {
14            for (int col = 0; col < width; ++col) {
15                // Map current position to its corresponding position in the base tile
16                int tileRow = row % sideLength;
17                int tileCol = col % sideLength;
18              
19                // Convert 2D position to 1D index for the frequency array
20                int tileIndex = tileRow * sideLength + tileCol;
21              
22                // Increment the frequency count for this tile position
23                ++frequency[tileIndex];
24            }
25        }
26      
27        // Sort frequencies in descending order to prioritize positions that appear most often
28        sort(frequency.rbegin(), frequency.rend());
29      
30        // Place ones in the maxOnes most frequent positions to maximize the total count
31        int totalOnes = 0;
32        for (int i = 0; i < maxOnes; ++i) {
33            totalOnes += frequency[i];
34        }
35      
36        return totalOnes;
37    }
38};
39
1/**
2 * Calculates the maximum number of ones that can be placed in a matrix
3 * with constraints on submatrices of size sideLength x sideLength
4 * 
5 * @param width - Width of the matrix
6 * @param height - Height of the matrix
7 * @param sideLength - Side length of each submatrix constraint
8 * @param maxOnes - Maximum number of ones allowed in each submatrix
9 * @return Maximum total number of ones that can be placed
10 */
11function maximumNumberOfOnes(width: number, height: number, sideLength: number, maxOnes: number): number {
12    // Store side length in a more readable variable
13    const submatrixSize: number = sideLength;
14  
15    // Create an array to count occurrences of each position within a submatrix
16    // The array size is submatrixSize * submatrixSize (all possible positions in a submatrix)
17    const positionCounts: number[] = new Array(submatrixSize * submatrixSize).fill(0);
18  
19    // Iterate through all positions in the full matrix
20    for (let row = 0; row < width; ++row) {
21        for (let col = 0; col < height; ++col) {
22            // Map the current position to its corresponding position within a submatrix
23            // using modulo to find the relative position
24            const submatrixPosition: number = (row % submatrixSize) * submatrixSize + (col % submatrixSize);
25          
26            // Increment the count for this submatrix position
27            ++positionCounts[submatrixPosition];
28        }
29    }
30  
31    // Sort the counts in descending order to prioritize positions that appear most frequently
32    positionCounts.sort((a: number, b: number) => b - a);
33  
34    // Select the top maxOnes positions with highest counts and sum them
35    // This gives us the maximum number of ones we can place
36    return positionCounts.slice(0, maxOnes).reduce((sum: number, count: number) => sum + count, 0);
37}
38

Time and Space Complexity

Time Complexity: O(width Γ— height)

The algorithm iterates through every position in the matrix exactly once using two nested loops - the outer loop runs width times and the inner loop runs height times. For each position (i, j), it performs constant time operations: calculating the modulo, computing index k, and incrementing a counter. The sorting operation takes O(sideLengthΒ² Γ— log(sideLengthΒ²)) time, but since sideLength ≀ min(width, height), this is bounded by O(width Γ— height). Therefore, the overall time complexity is O(width Γ— height), which matches the reference answer's O(m Γ— n) where m and n represent the dimensions of the matrix.

Space Complexity: O(sideLengthΒ²)

The algorithm uses an array cnt of size sideLength Γ— sideLength to store the frequency counts for each unique position within a sideLength Γ— sideLength subgrid. This is the dominant space usage in the algorithm, as all other variables use constant space. Therefore, the space complexity is O(sideLengthΒ²).

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

Common Pitfalls

1. Confusion Between Matrix Dimensions and Iteration Order

A critical pitfall in this solution is mixing up the iteration variables with matrix dimensions. The problem states the matrix has dimensions width Γ— height, which typically means width columns and height rows. However, the code iterates using:

for row in range(width):
    for col in range(height):

This treats width as the number of rows and height as the number of columns, which is counterintuitive and could lead to incorrect results if the matrix is not square.

Solution: Use clearer variable naming and iterate consistently with the matrix dimensions:

# Iterate through all cells in the height Γ— width matrix
for row in range(height):
    for col in range(width):
        pattern_position = (row % sideLength) * sideLength + (col % sideLength)
        position_frequencies[pattern_position] += 1

2. Incorrect Pattern Position Calculation

Another potential pitfall is incorrectly mapping 2D coordinates to 1D array indices. The formula (row % sideLength) * sideLength + (col % sideLength) assumes row-major ordering, but developers might accidentally use column-major ordering or mix up the formula.

Solution: Add validation or use a helper function to make the mapping explicit:

def get_pattern_index(row, col, sideLength):
    """Convert 2D pattern position to 1D array index."""
    pattern_row = row % sideLength
    pattern_col = col % sideLength
    return pattern_row * sideLength + pattern_col

3. Edge Case: maxOnes Greater Than sideLengthΒ²

If maxOnes > sideLength * sideLength, the code would work correctly but it's worth noting that this means we can fill the entire matrix with ones since the constraint becomes meaningless.

Solution: Add a optimization check at the beginning:

if maxOnes >= sideLength * sideLength:
    return width * height  # Can place 1s everywhere

4. Integer Overflow in Large Inputs

For very large matrices, the frequency counts could potentially overflow in languages with fixed-size integers (though Python handles this automatically).

Solution: In Python this isn't an issue, but in other languages, ensure you're using appropriate data types for large values or add bounds checking.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm is best for finding the shortest distance between two points in an unweighted graph?


Recommended Readings

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

Load More