每日算法 - 2024-05-13
记录今天学习的算法题解。
2335. 装满杯子需要的最短总时长
题目
思路
贪心
这道题的关键在于每次操作尽可能多地减少杯子的数量。我们每次操作可以装一杯或两杯(不同类型)。为了最小化总时间,应该优先选择装两杯不同类型的水。
解题过程
问题的最优解受到两个因素的制约:
- 数量最多的那杯水:假设数量最多的水需要
max_val
杯。由于每次操作最多只能装一杯这种类型的水,所以我们至少需要max_val
秒才能把这种水装完。这是一个时间的下限。 - 总水量:假设总水量为
total_sum = amount[0] + amount[1] + amount[2]
。由于每次操作最多装两杯水,装完total_sum
杯水至少需要ceil(total_sum / 2)
秒。这里的ceil
表示向上取整,因为如果总数是奇数,最后一次操作只能装一杯。在整数除法中,(total_sum + 1) / 2
可以实现向上取整的效果。这也是一个时间的下限。
最短的总时长必须同时满足这两个下限条件。因此,最终答案就是这两个下限中的较大值:max(max_val, (total_sum + 1) / 2)
。
可以证明,总是存在一种贪心策略(例如,每次都优先选择数量最多的两种水来装)可以达到这个下限。
复杂度
- 时间复杂度: O ( 1 ) O(1) O(1)。只需要进行常数次的比较和算术运算。
- 空间复杂度: O ( 1 ) O(1) O(1)。只需要常数级别的额外空间存储变量。
Code
class Solution {public int fillCups(int[] amount) {// 找到数量最多的杯子数int maxVal = 0;int totalSum = 0;for (int count : amount) {maxVal = Math.max(maxVal, count);totalSum += count;}int avgOps = (totalSum + 1) / 2; return Math.max(maxVal, avgOps);}
}
1753. 移除石子的最大得分
题目
思路
贪心
目标是最大化操作次数(每次操作得1分)。每次操作需要从两堆不同的非空石子堆中各取一个。为了尽可能多地进行操作,我们应该避免让某一堆石子过早地被单独剩下。因此,贪心策略是每次都选择当前石子数最多的两堆进行操作。
解题过程
为了方便分析,我们先对三堆石子的数量 a, b, c
进行排序,假设排序后为 min_val <= mid_val <= max_val
。
考虑贪心策略:每次都从最多的两堆(max_val
和 mid_val
)中取石子。
分析两种情况:
-
max_val >= mid_val + min_val
:
在这种情况下,最大堆的石子数量大于等于另外两堆之和。我们可以将mid_val
堆和min_val
堆的石子完全与max_val
堆配对消耗完。 -
max_val < mid_val + min_val
:
在这种情况下,最大堆的石子数不足以单独消耗掉另外两堆。这意味着我们可以持续地从最大的两堆中取石子,直到所有石子几乎被取完(最多只会剩下一个石子)。
总石子数为S = max_val + mid_val + min_val
。每次操作减少2个石子。我们可以进行的操作次数取决于总石子数S
。- 如果
S
是偶数,最多可以进行S / 2
次操作,最后没有石子剩下。 - 如果
S
是奇数,最多可以进行(S - 1) / 2
次操作,最后会剩下1个石子。
这两种情况合并起来就是floor(S / 2)
次操作。在整数除法中,(max_val + mid_val + min_val) / 2
正好计算出floor(S / 2)
。
因此,最大得分为(max_val + mid_val + min_val) / 2
。
- 如果
综合这两种情况,即为题解。
复杂度
- 时间复杂度: O ( 1 ) O(1) O(1)。对三个数排序(或找到最大、最小、中间值)以及后续计算都是常数时间。
- 空间复杂度: O ( 1 ) O(1) O(1)。只需要常数级别的额外空间。
Code
class Solution {public int maximumScore(int a, int b, int c) {int max = Math.max(a, Math.max(b, c));int min = Math.min(a, Math.min(b, c));int mid = (a + b + c) - (max + min);if (max >= mid + min) {return mid + min;} else {return (max + mid + min) / 2;}}
}
2592. 最大化数组的伟大值(复习)
题目
这是第二次写这道题了,典型的田忌赛马问题,写的还不错,就不多说了,详细题解可以参考之前的笔记:每日算法-250428
Code
class Solution {public int maximizeGreatness(int[] nums) {int ret = 0;Arrays.sort(nums);int left = 0, right = nums.length - 1, i = right;while (left <= right) {if (nums[i] >= nums[right]) {left++;} else {ret++;right--;}i--;}return ret;}
}