LeetCode第222场周赛
题目:5641. 卡车上的最大单元数
请你将一些箱子装在 一辆卡车 上。给你一个二维数组 boxTypes ,其中 boxTypes[i] = [numberOfBoxesi, numberOfUnitsPerBoxi] :
numberOfBoxesi 是类型 i 的箱子的数量。
numberOfUnitsPerBoxi 是类型 i 每个箱子可以装载的单元数量。
整数 truckSize 表示卡车上可以装载 箱子 的 最大数量 。只要箱子数量不超过 truckSize ,你就可以选择任意箱子装到卡车上。
返回卡车可以装载 单元 的 最大 总数。
示例 1:
输入:boxTypes = [[1,3],[2,2],[3,1]], truckSize = 4
输出:8
解释:箱子的情况如下:
- 1 个第一类的箱子,里面含 3 个单元。
- 2 个第二类的箱子,每个里面含 2 个单元。
- 3 个第三类的箱子,每个里面含 1 个单元。
可以选择第一类和第二类的所有箱子,以及第三类的一个箱子。
单元总数 = (1 * 3) + (2 * 2) + (1 * 1) = 8
示例 2:
输入:boxTypes = [[5,10],[2,5],[4,7],[3,9]], truckSize = 10
输出:91
提示:
1 <= boxTypes.length <= 1000
1 <= numberOfBoxesi, numberOfUnitsPerBoxi <= 1000
1 <= truckSize <= 106
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-units-on-a-truck
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码:贪心
题目要我们求卡车可以装载单元的最大总数,所以对于所有箱子,我们优先选可以装载的单元数量多的。
class Solution {
public int maximumUnits(int[][] boxTypes, int truckSize) {
int n = boxTypes.length;
Arrays.sort(boxTypes, (o1, o2) -> o2[1] - o1[1]);
int ans = 0;
for(int i = 0; i < n; i++) {
int cur = Math.min(truckSize, boxTypes[i][0]);
ans += cur * boxTypes[i][1];
truckSize -= cur;
}
return ans;
}
}
题目:5642. 大餐计数
大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 2 的幂。
你可以搭配 任意 两道餐品做一顿大餐。
给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i 道餐品的美味程度,返回你可以用数组中的餐品做出的不同 大餐 的数量。结果需要对 109 + 7 取余。
注意,只要餐品下标不同,就可以认为是不同的餐品,即便它们的美味程度相同。
示例 1:
输入:deliciousness = [1,3,5,7,9]
输出:4
解释:大餐的美味程度组合为 (1,3) 、(1,7) 、(3,5) 和 (7,9) 。
它们各自的美味程度之和分别为 4 、8 、8 和 16 ,都是 2 的幂。
示例 2:
输入:deliciousness = [1,1,1,3,3,3,7]
输出:15
解释:大餐的美味程度组合为 3 种 (1,1) ,9 种 (1,3) ,和 3 种 (1,7) 。
提示:
1 <= deliciousness.length <= 10^5
0 <= deliciousness[i] <= 2^20
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-good-meals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码:
题目中给出的deliciousness 的最大范围时220,所以我们要求的2的幂最大到221(2^20 + 2^20)。
对于deliciousness[i] 我们遍历一边所有的2的幂(s),然后在数组中查找有多少(s - deliciousness[i])。
class Solution {
public int countPairs(int[] d) {
int MOD = (int) (1e9 + 7);
int n = d.length;
int ans = 0;
Arrays.sort(d);
HashMap<Integer, Integer> m = new HashMap<>();
for(int i = 0; i < n; i++) {
for(int j = 0; j < 22; j++) {
if((1 << j) >= d[i] && m.containsKey((1 << j) - d[i])) {
ans = (ans + m.get((1 << j) - d[i])) % MOD;
}
}
m.put(d[i], m.getOrDefault(d[i], 0) + 1);
}
return ans;
}
}
题目:5643. 将数组分成三个子数组的方案数
我们称一个分割整数数组的方案是 好的 ,当它满足:
数组被分成三个 非空 连续子数组,从左至右分别命名为 left , mid , right 。
left 中元素和小于等于 mid 中元素和,mid 中元素和小于等于 right 中元素和。
给你一个 非负 整数数组 nums ,请你返回 好的 分割 nums 方案数目。由于答案可能会很大,请你将结果对 109 + 7 取余后返回。
示例 1:
输入:nums = [1,1,1]
输出:1
解释:唯一一种好的分割方案是将 nums 分成 [1] [1] [1] 。
示例 2:
输入:nums = [1,2,2,2,5,0]
输出:3
解释:nums 总共有 3 种好的分割方案:
[1] [2] [2,2,5,0]
[1] [2,2] [2,5,0]
[1,2] [2,2] [5,0]
示例 3:
输入:nums = [3,2,1]
输出:0
解释:没有好的分割方案。
提示:
3 <= nums.length <= 105
0 <= nums[i] <= 104
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ways-to-split-array-into-three-subarrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码:前缀和+二分
遍历所有方案数,也就遍历将数组分成三部分的两点,求每个部分的和我们可以用前缀和在O(1)的时间内求出。遍历所有方案数的时间时n方的。题目中给出所有的数都是正整数,所以我们可以用二分来查找。
- 确定第一个点,将数组分成左右两部分,在确定第二个点的范围。
- 查找所有left <= mid 最左的点(命名为:l)
- 查找所有mid <= right 最右的点(命名为:r)
- 对于每个点的所有方案数就是 r - l
class Solution {
public int waysToSplit(int[] nums) {
int MOD = (int) (1e9 + 7);
int n = nums.length;
int[] sum = new int[n];
sum[0] = nums[0];
for(int i = 1; i < n; i++){
sum[i] = sum[i - 1] + nums[i];
}
int ans = 0;
for(int i = 0; i < n - 2; i++) {
int l = i + 1, r = n - 2;
while(l < r) {
int mid = l + r >> 1;
if(sum[mid] - sum[i] >= sum[i]) r = mid;
else l = mid + 1;
}
if(sum[l] - sum[i] >= sum[i]) {
int q = l;
r = n - 2;
while (l < r) {
int mid = l + r + 1 >> 1;
if (sum[mid] - sum[i] <= sum[n - 1] - sum[mid]) l = mid;
else r = mid - 1;
}
if(sum[n - 1] - sum[l] >= sum[l] - sum[i]) {
ans = (ans + l - q + 1) % MOD;
}
}
}
return ans;
}
}
题目:5644. 得到子序列的最少操作次数
给你一个数组 target ,包含若干 互不相同 的整数,以及另一个整数数组 arr ,arr 可能 包含重复元素。
每一次操作中,你可以在 arr 的任意位置插入任一整数。比方说,如果 arr = [1,4,1,2] ,那么你可以在中间添加 3 得到 [1,4,3,1,2] 。你可以在数组最开始或最后面添加整数。
请你返回 最少 操作次数,使得 target 成为 arr 的一个子序列。
一个数组的 子序列 指的是删除原数组的某些元素(可能一个元素都不删除),同时不改变其余元素的相对顺序得到的数组。比方说,[2,7,4] 是 [4,2,3,7,2,1,4] 的子序列(加粗元素),但 [2,4,2] 不是子序列。
示例 1:
输入:target = [5,1,3], arr = [9,4,2,3,4]
输出:2
解释:你可以添加 5 和 1 ,使得 arr 变为 [5,9,4,1,2,3,4] ,target 为 arr 的子序列。
示例 2:
输入:target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1]
输出:3
提示:
1 <= target.length, arr.length <= 105
1 <= target[i], arr[i] <= 109
target 不包含任何重复元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-operations-to-make-a-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码:最长递增子序列
最长公共子序列 =》 最长递增子序列
求最长递增子序列可以使用贪心在 O(nlogn)的时间内求出来。
求最长递增子序列:
- 维护一个数组a,数组中的元素递增。
- 当新进来一个数时,用二分查找第一个大于等于他的数,并将他替换掉。
class Solution {
public int minOperations(int[] target, int[] arr) {
int n = target.length;
HashMap<Integer, Integer> m = new HashMap<>();
for(int i = 0; i < n; i++) {
m.put(target[i], i);
}
n = arr.length;
int[] a = new int[n];
Arrays.fill(a, Integer.MAX_VALUE);
for(int i = 0; i < n; i++) {
if(m.containsKey(arr[i])) {
int x = m.get(arr[i]);
int l = 0, r = n;
while(l < r) {
int mid = l + r >> 1;
if(a[mid] >= x) r = mid;
else l = mid + 1;
}
a[l] = x;
}
}
int l = 0, r = n;
while(l < r) {
int mid = l + r >> 1;
if(a[mid] >= Integer.MAX_VALUE) r = mid;
else l = mid + 1;
}
return target.length - l;
}
}