Facebook Pixel

2554. Maximum Number of Integers to Choose From a Range I

Problem Description

You need to select integers from the range [1, n] while following specific rules to maximize the count of selected integers.

The rules are:

  • You can only choose integers between 1 and n (inclusive)
  • Each integer can be selected at most once (no repetition)
  • You cannot choose any integer that appears in the banned array
  • The sum of all chosen integers must not exceed maxSum

Your goal is to find the maximum number of integers you can choose while satisfying all these constraints.

For example, if n = 5, banned = [2, 4], and maxSum = 6, you could choose integers 1 and 3 (sum = 4), or just 5 (sum = 5), or 1 and 5 (sum = 6). The maximum count would be 2 integers (1 and 5).

The solution uses a greedy approach by trying to select the smallest available integers first. This strategy works because choosing smaller integers leaves more room in the sum budget for additional integers. The algorithm iterates through integers from 1 to n, skipping banned integers, and keeps adding valid integers to the selection as long as the running sum doesn't exceed maxSum.

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

Intuition

To maximize the number of integers we can choose, we need to think about which integers would allow us to pick the most items while staying within the maxSum limit.

If we want to maximize the count, we should prefer smaller integers over larger ones. Why? Because smaller integers consume less of our sum budget, leaving more room for additional integers. For instance, choosing 1, 2, 3 (sum = 6) gives us 3 integers, while choosing just 6 (sum = 6) gives us only 1 integer.

This naturally leads to a greedy strategy: start from the smallest possible integer (which is 1) and keep adding the next smallest available integer as long as we don't exceed maxSum.

The key insight is that there's no benefit to skipping a smaller valid integer in favor of a larger one if our goal is to maximize the count. If we can afford to add integer i to our sum without exceeding maxSum, we should always take it, because:

  1. Taking i increases our count by 1
  2. Not taking i doesn't help us pick more integers later, since all subsequent integers are larger and will consume even more of our budget

By processing integers in ascending order from 1 to n, skipping those in the banned set, and greedily selecting each valid integer that fits within our remaining budget, we guarantee the maximum possible count.

Learn more about Greedy, Binary Search and Sorting patterns.

Solution Approach

The implementation follows a greedy enumeration strategy with the help of a hash set for efficient lookups.

Data Structure Setup:

  • Convert the banned array into a hash set ban for O(1) lookup time when checking if an integer is banned
  • Initialize s = 0 to track the running sum of selected integers
  • Initialize ans = 0 to count how many integers we've selected

Main Algorithm:

  1. Iterate through integers from 1 to n in ascending order using a for loop
  2. For each integer i:
    • First check if adding i would exceed our budget: if s + i > maxSum, we break out of the loop since all subsequent integers will be even larger
    • If i is not in the banned set (i not in ban), we can select it:
      • Increment the count: ans += 1
      • Add i to the running sum: s += i
    • If i is banned, we simply skip it and continue to the next integer

Why This Works:

  • By processing integers in ascending order, we ensure we're always trying to add the smallest available integer first
  • The early termination condition (s + i > maxSum) is an optimization - once we can't afford the current integer, we know we can't afford any larger integers either
  • Using a set for banned integers makes the lookup operation efficient, keeping the overall time complexity at O(n)

The algorithm terminates either when we've checked all integers from 1 to n, or when adding the next integer would exceed maxSum. The final value of ans gives us the maximum number of integers we can choose.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through an example with n = 7, banned = [3, 5], and maxSum = 10.

Setup:

  • Convert banned = [3, 5] into a set for O(1) lookups: ban = {3, 5}
  • Initialize s = 0 (running sum) and ans = 0 (count of selected integers)

Iteration Process:

is + is + i ≤ 10?i in ban?Actionsans
10 + 1 = 1YesNoSelect 111
21 + 2 = 3YesNoSelect 232
33 + 3 = 6YesYesSkip (banned)32
43 + 4 = 7YesNoSelect 473
57 + 5 = 12No-Break (exceeds maxSum)73

Step-by-step breakdown:

  1. i = 1: Sum would be 1, which is ≤ 10, and 1 is not banned → Select it
  2. i = 2: Sum would be 3, which is ≤ 10, and 2 is not banned → Select it
  3. i = 3: Sum would be 6, which is ≤ 10, but 3 is banned → Skip it
  4. i = 4: Sum would be 7, which is ≤ 10, and 4 is not banned → Select it
  5. i = 5: Sum would be 12, which exceeds 10 → Break (no need to check larger integers)

Result: We selected integers {1, 2, 4} with a sum of 7 and a count of 3.

This example demonstrates how the greedy approach maximizes the count by:

  • Selecting smaller integers first (1, 2, 4 instead of larger alternatives)
  • Efficiently skipping banned integers
  • Terminating early when the budget is exceeded

Solution Implementation

1class Solution:
2    def maxCount(self, banned: List[int], n: int, maxSum: int) -> int:
3        # Initialize count of selected numbers and running sum
4        count = 0
5        current_sum = 0
6      
7        # Convert banned list to set for O(1) lookup
8        banned_set = set(banned)
9      
10        # Iterate through numbers from 1 to n
11        for num in range(1, n + 1):
12            # Check if adding current number would exceed maxSum
13            if current_sum + num > maxSum:
14                break
15          
16            # If number is not banned, include it in our selection
17            if num not in banned_set:
18                count += 1
19                current_sum += num
20      
21        return count
22
1class Solution {
2    /**
3     * Finds the maximum count of integers from 1 to n that can be chosen
4     * such that their sum doesn't exceed maxSum and they are not in the banned array.
5     * 
6     * @param banned array of integers that cannot be chosen
7     * @param n upper limit of the range [1, n] to choose from
8     * @param maxSum maximum allowed sum of chosen integers
9     * @return maximum count of valid integers that can be chosen
10     */
11    public int maxCount(int[] banned, int n, int maxSum) {
12        // Create a HashSet to store banned numbers for O(1) lookup
13        Set<Integer> bannedSet = new HashSet<>(banned.length);
14        for (int bannedNumber : banned) {
15            bannedSet.add(bannedNumber);
16        }
17      
18        // Initialize count of chosen numbers and running sum
19        int count = 0;
20        int currentSum = 0;
21      
22        // Iterate through integers from 1 to n
23        // Continue while within range and adding next number won't exceed maxSum
24        for (int currentNumber = 1; currentNumber <= n && currentSum + currentNumber <= maxSum; currentNumber++) {
25            // Check if current number is not banned
26            if (!bannedSet.contains(currentNumber)) {
27                // Increment count and add to running sum
28                count++;
29                currentSum += currentNumber;
30            }
31        }
32      
33        return count;
34    }
35}
36
1class Solution {
2public:
3    int maxCount(vector<int>& banned, int n, int maxSum) {
4        // Create a hash set for O(1) lookup of banned numbers
5        unordered_set<int> bannedSet(banned.begin(), banned.end());
6      
7        // Initialize count of valid numbers and current sum
8        int count = 0;
9        int currentSum = 0;
10      
11        // Iterate through numbers from 1 to n
12        for (int i = 1; i <= n; ++i) {
13            // Check if adding current number would exceed maxSum
14            if (currentSum + i > maxSum) {
15                break;
16            }
17          
18            // If current number is not banned, include it
19            if (bannedSet.find(i) == bannedSet.end()) {
20                count++;
21                currentSum += i;
22            }
23        }
24      
25        return count;
26    }
27};
28
1/**
2 * Finds the maximum count of integers from 1 to n that can be chosen
3 * such that none are in the banned list and their sum doesn't exceed maxSum
4 * @param banned - Array of banned integers that cannot be chosen
5 * @param n - Upper limit of the range [1, n] to choose from
6 * @param maxSum - Maximum allowed sum of chosen integers
7 * @returns Maximum count of integers that can be chosen
8 */
9function maxCount(banned: number[], n: number, maxSum: number): number {
10    // Create a set for O(1) lookup of banned numbers
11    const bannedSet: Set<number> = new Set<banned);
12  
13    // Track the running sum of chosen numbers
14    let currentSum: number = 0;
15  
16    // Track the count of chosen numbers
17    let count: number = 0;
18  
19    // Iterate through all integers from 1 to n
20    for (let currentNumber: number = 1; currentNumber <= n; currentNumber++) {
21        // Check if adding current number would exceed maxSum
22        if (currentNumber + currentSum > maxSum) {
23            break;
24        }
25      
26        // Skip if current number is in the banned list
27        if (bannedSet.has(currentNumber)) {
28            continue;
29        }
30      
31        // Add current number to sum and increment count
32        currentSum += currentNumber;
33        count++;
34    }
35  
36    return count;
37}
38

Time and Space Complexity

The time complexity is O(n), where n is the given integer parameter. The algorithm iterates through integers from 1 to n in the worst case, performing constant-time operations (set lookup, addition, and comparison) for each integer.

The space complexity is O(m), where m is the length of the banned list. The algorithm creates a set from the banned list, which requires O(m) space to store all banned elements. The reference answer states O(n) for space complexity, which would be correct if we consider the worst-case scenario where the banned list could contain up to n elements (all integers from 1 to n), making m = O(n).

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

Common Pitfalls

1. Not Converting Banned Array to Set

A critical performance pitfall is directly checking membership in the banned list instead of converting it to a set first. This leads to O(m) lookup time for each check, where m is the length of the banned array.

Incorrect approach:

def maxCount(self, banned: List[int], n: int, maxSum: int) -> int:
    count = 0
    current_sum = 0
  
    for num in range(1, n + 1):
        if current_sum + num > maxSum:
            break
        # O(m) lookup - inefficient!
        if num not in banned:  
            count += 1
            current_sum += num
  
    return count

Why it's problematic: The time complexity becomes O(n * m) instead of O(n + m), which can cause timeout for large inputs.

2. Checking Banned Status Before Sum Validation

Another pitfall is checking if a number is banned before verifying if it can fit within the sum constraint. This can lead to unnecessary set lookups.

Inefficient ordering:

def maxCount(self, banned: List[int], n: int, maxSum: int) -> int:
    count = 0
    current_sum = 0
    banned_set = set(banned)
  
    for num in range(1, n + 1):
        # Checking banned status first - unnecessary lookup if sum exceeds
        if num not in banned_set:
            if current_sum + num > maxSum:
                break
            count += 1
            current_sum += num
  
    return count

Problem: When current_sum + num > maxSum, we should terminate immediately without checking the banned set. The above code might continue iterating through banned numbers unnecessarily.

3. Not Handling Edge Cases with Empty Banned Array

While Python handles empty sets gracefully, explicitly checking for edge cases can improve code clarity and prevent potential issues in other languages.

Robust solution:

def maxCount(self, banned: List[int], n: int, maxSum: int) -> int:
    count = 0
    current_sum = 0
  
    # Handle empty banned array efficiently
    banned_set = set(banned) if banned else set()
  
    for num in range(1, n + 1):
        # Check sum constraint first
        if current_sum + num > maxSum:
            break
      
        # Then check if not banned
        if num not in banned_set:
            count += 1
            current_sum += num
  
    return count

4. Integer Overflow Concerns

In languages with fixed integer sizes, adding large numbers could cause overflow. While Python handles arbitrary precision integers automatically, being mindful of this is important for language portability.

Best practice: Always validate that current_sum + num won't exceed the maximum integer value in languages like Java or C++, or use appropriate data types (like long in Java).

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

How does quick sort divide the problem into subproblems?


Recommended Readings

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

Load More