leetcode
leetcode 2201 ~ 2250
从一个范围内选择最多整数 I

从一个范围内选择最多整数 I

难度:

标签:

题目描述

You are given an integer array banned and two integers n and maxSum. You are choosing some number of integers following the below rules:

  • The chosen integers have to be in the range [1, n].
  • Each integer can be chosen at most once.
  • The chosen integers should not be in the array banned.
  • The sum of the chosen integers should not exceed maxSum.

Return the maximum number of integers you can choose following the mentioned rules.

 

Example 1:

Input: banned = [1,6,5], n = 5, maxSum = 6
Output: 2
Explanation: You can choose the integers 2 and 4.
2 and 4 are from the range [1, 5], both did not appear in banned, and their sum is 6, which did not exceed maxSum.

Example 2:

Input: banned = [1,2,3,4,5,6,7], n = 8, maxSum = 1
Output: 0
Explanation: You cannot choose any integer while following the mentioned conditions.

Example 3:

Input: banned = [11], n = 7, maxSum = 50
Output: 7
Explanation: You can choose the integers 1, 2, 3, 4, 5, 6, and 7.
They are from the range [1, 7], all did not appear in banned, and their sum is 28, which did not exceed maxSum.

 

Constraints:

  • 1 <= banned.length <= 104
  • 1 <= banned[i], n <= 104
  • 1 <= maxSum <= 109

代码结果

运行时间: 90 ms, 内存: 17.7 MB


// 思路:
// 1. 使用Java Stream生成1到n的数字流。
// 2. 过滤掉banned中的数字。
// 3. 在限制的maxSum内累加选择的数字,并统计数量。

import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
import java.util.stream.IntStream;

public class Solution {
    public int maxCount(int[] banned, int n, int maxSum) {
        Set<Integer> bannedSet = new HashSet<>(Arrays.asList(Arrays.stream(banned).boxed().toArray(Integer[]::new)));
        int[] sum = {0};
        return (int) IntStream.rangeClosed(1, n)
                .filter(i -> !bannedSet.contains(i) && (sum[0] += i) <= maxSum)
                .count();
    }
}

解释

方法:

这道题的思路是贪心算法。首先,将banned数组转换为集合s,便于快速查询某个整数是否被禁止。然后,从1开始遍历到n,对于每一个整数i,如果i不在s中且i小于等于maxSum,那么就将i加入到选择中,并且从maxSum中减去i,同时增加选择的整数数量ans。遍历结束后,ans即为最多可以选择的整数数目。

时间复杂度:

O(n)

空间复杂度:

O(banned.length)

代码细节讲解

🦆
为什么选择将banned数组转换为集合而不是其他数据结构,如列表或字典?
将banned数组转换为集合的主要原因是集合(set)在Python中基于哈希表实现,因此查找操作的平均时间复杂度是O(1)。这使得在遍历整数时,检查一个数字是否被禁止变得非常高效。如果使用列表,查找操作的时间复杂度将是O(n),这会导致总体算法效率降低。尽管字典也提供O(1)的查找效率,但它主要用于存储键值对,而在这种情况下,我们只需要一组唯一的值,因此集合是更合适的选择。
🦆
在遍历整数时,如果当前整数i大于maxSum就立即停止遍历,这种方法是否可能错过一些有效的选择?例如,如果后面的数字较小且未被禁止,是否还有机会继续选择?
在这个算法中,由于我们从1开始递增地遍历到n,并且在每一步中尝试添加尽可能小的整数到选择中,一旦当前整数i大于剩余的maxSum,即使后续有更小的、未被禁止的整数也不可能被选中,因为所有后续整数都至少等于当前的i。因此,当i大于maxSum时停止遍历不会错过任何有效的选择。
🦆
具体是如何保证这种贪心算法选择的整数数目是最大可能的?是否存在证明或是特定的逻辑来支持这一点?
这个贪心算法的基本思路是尽可能选择最小的、未被禁止的整数,以便使总和不超过maxSum。选择最小的数可以确保选择的整数数量最大化,因为用较小的数替代较大的数可以留出更多的空间来添加更多的数。这种策略的正确性基于这样一个事实:替换任何已选择的较大数(未超过maxSum且未被禁止)以获得更小的数总能增加可选择的数的数量,或至少保持不变。因此,这种策略可以保证选择的整数数量是最大的。
🦆
算法中没有显式处理所有banned元素都在[1, n]范围外的情况,这是否会影响算法的准确性或效率?
算法中将banned数组转换为集合的步骤并没有限定元素必须在[1, n]的范围内,这意味着集合s可能包含一些超出这个范围的元素。然而,这并不影响算法的准确性,因为在遍历和选择整数时,只考虑那些在[1, n]范围内且未被禁止的数。集合中超出范围的元素在遍历过程中不会被访问,因此不会影响结果。从效率的角度看,这可能略微增加了转换banned数组到集合的时间和空间成本,但这通常是可以接受的,除非banned数组非常大且大部分元素都在范围外。

相关问题