每日算法-250420
每日算法 - 2025/04/20
记录一下今天刷的几道 LeetCode 题目。
3075. 幸福值最大化的选择方案
题目描述
思路
贪心
解题过程
核心思想是:为了总幸福值最大化,我们应该优先选择当前能提供最大幸福值的孩子。
- 排序:将
happiness
数组降序排序。这样,每次我们考虑的就是当前可选孩子中幸福值最高的。 - 选择与更新:我们总共需要选择
k
个孩子。- 选择当前幸福值最高的孩子(排序后的数组从左到右选择)。
- 设我们已经选择了
i
个孩子(i
从 0 开始计数),那么在选择第i+1
个孩子时,他的原始幸福值happiness[i]
会因为前面已经选择了i
个孩子而减少i
。 - 所以,第
i+1
个孩子(数组索引为i
)实际贡献的幸福值是max(0, happiness[i] - i)
,因为幸福值不能为负数。 - 累加这个实际贡献的幸福值。
- 迭代:重复步骤 2,直到选满
k
个孩子或者所有可选孩子的实际幸福值都变为 0。
复杂度
- 时间复杂度: O ( N log N ) O(N \log N) O(NlogN),主要由排序决定,其中 N 是
happiness
数组的长度。 - 空间复杂度: O ( log N ) O(\log N) O(logN) 或 O ( N ) O(N) O(N),取决于排序算法使用的栈空间或辅助空间。如果只考虑额外空间(不计输入数组的修改),可以认为是 O ( 1 ) O(1) O(1)。
Code
class Solution {public long maximumHappinessSum(int[] happiness, int k) {// 对幸福值数组进行升序排序Arrays.sort(happiness);int n = happiness.length;long maxHappinessSum = 0;int turns = 0; // 记录已经选择了多少个孩子 (即轮数)// 从幸福值最高的孩子开始选择 (数组末尾)for (int i = n - 1; i >= 0 && k > 0; i--) {// 计算当前孩子实际能贡献的幸福值// 原始幸福值 happiness[i] 需要减去已经进行的轮数 turnslong currentHappiness = happiness[i] - turns;// 幸福值不能小于 0if (currentHappiness > 0) {maxHappinessSum += currentHappiness;} else {// 如果当前最大幸福值的孩子贡献都 <= 0,后续的更不可能 > 0break;}turns++; // 轮数增加k--; // 还需要选择的孩子数量减少}return maxHappinessSum;}
}
2554. 从一个范围内选择最多整数 I
题目描述
思路
贪心
解题过程
目标是在不超过 maxSum
的前提下,从范围 [1, n]
中选择尽可能多的、不在 banned
列表中的整数。
- 贪心策略:为了选择最多的整数,我们应该优先选择数值最小的整数,因为它们消耗的
maxSum
最少。 - 排除禁用数:使用一个数组来快速判断一个数是否在
banned
列表中。 - 迭代选择:从 1 开始,依次检查每个整数
i
(1 <= i <= n
):- 如果
i
不在banned
列表中。 - 并且当前
maxSum
大于0
。 - 那么就选择
i
,增加计数ret
,并更新maxSum -= i
。
- 如果
- 返回结果:最终的
ret
就是最多能选择的整数数量。
复杂度
- 时间复杂度: O ( B + n ) O(B + n) O(B+n),其中 B 是
banned
数组的长度(构建哈希表或标记数组),n 是上限范围(遍历1
到n
)。 - 空间复杂度: O ( B ) O(B) O(B) 如果使用
HashSet
,或者 O ( S ) O(S) O(S) 如果使用大小为S
(如 10001 或 n+1) 的数组来标记banned
数字。
Code
class Solution {public int maxCount(int[] banned, int n, int maxSum) {int size = 10001;int[] hash = new int[size];for (int x : banned) {hash[x]++;}int ret = 0, sum = 0;for (int i = 1; i <= n && maxSum >= 0; i++) {if (hash[i] == 0) {ret++;maxSum -= i;}}return maxSum < 0 ? ret - 1 : ret;}
}
1283. 使结果不超过阈值的最小除数
题目描述
这是第二次做这道题,思路已经比较清晰了。详细题解可以参考之前的笔记 每日算法-250408。
Code
class Solution {public int smallestDivisor(int[] nums, int threshold) {Arrays.sort(nums);int left = 1, right = nums[nums.length - 1];while (left <= right) {int mid = left + (right - left) / 2;if (check(nums, mid, threshold)) {right = mid - 1;} else {left = mid + 1;}}return left;}private boolean check(int[] arr, int divisor, int k) {int sum = 0;for (int i = 0; i < arr.length; i++) {if (arr[i] % divisor == 0) {sum += arr[i] / divisor;} else {sum += arr[i] / divisor;sum++;}}return sum <= k;}
}