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
andn
(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
.
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:
- Taking
i
increases our count by 1 - 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 setban
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:
- Iterate through integers from
1
ton
in ascending order using a for loop - For each integer
i
:- First check if adding
i
would exceed our budget: ifs + 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
- Increment the count:
- If
i
is banned, we simply skip it and continue to the next integer
- First check if adding
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 EvaluatorExample 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) andans = 0
(count of selected integers)
Iteration Process:
i | s + i | s + i ≤ 10? | i in ban? | Action | s | ans |
---|---|---|---|---|---|---|
1 | 0 + 1 = 1 | Yes | No | Select 1 | 1 | 1 |
2 | 1 + 2 = 3 | Yes | No | Select 2 | 3 | 2 |
3 | 3 + 3 = 6 | Yes | Yes | Skip (banned) | 3 | 2 |
4 | 3 + 4 = 7 | Yes | No | Select 4 | 7 | 3 |
5 | 7 + 5 = 12 | No | - | Break (exceeds maxSum) | 7 | 3 |
Step-by-step breakdown:
- i = 1: Sum would be 1, which is ≤ 10, and 1 is not banned → Select it
- i = 2: Sum would be 3, which is ≤ 10, and 2 is not banned → Select it
- i = 3: Sum would be 6, which is ≤ 10, but 3 is banned → Skip it
- i = 4: Sum would be 7, which is ≤ 10, and 4 is not banned → Select it
- 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).
How does quick sort divide the problem into subproblems?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
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
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Don’t Miss This!