【刷题之路】LeetCode LCP 18. 早餐组合
一、题目描述
原题连接: LCP 18. 早餐组合
题目描述:
小扣在秋日市集选择了一家早餐摊位,一维整型数组 staple 中记录了每种主食的价格,一维整型数组 drinks 中记录了每种饮料的价格。小扣的计划选择一份主食和一款饮料,且花费不超过 x 元。请返回小扣共有多少种购买方案。
注意:答案需要以 1e9 + 7 (1000000007) 为底取模,如:计算初始结果为:1000000008,请返回 1
示例 1:
输入: staple = [10,20,5], drinks = [5,5,2], x = 15
输出: 6
解释:小扣有 6 种购买方案,所选主食与所选饮料在数组中对应的下标分别是:
第 1 种方案:staple[0] + drinks[0] = 10 + 5 = 15;
第 2 种方案:staple[0] + drinks[1] = 10 + 5 = 15;
第 3 种方案:staple[0] + drinks[2] = 10 + 2 = 12;
第 4 种方案:staple[2] + drinks[0] = 5 + 5 = 10;
第 5 种方案:staple[2] + drinks[1] = 5 + 5 = 10;
第 6 种方案:staple[2] + drinks[2] = 5 + 2 = 7。
示例 2:
输入: staple = [2,1,1], drinks = [8,9,5,1], x = 9
输出: 8
解释:小扣有 8 种购买方案,所选主食与所选饮料在数组中对应的下标分别是:
第 1 种方案:staple[0] + drinks[2] = 2 + 5 = 7;
第 2 种方案:staple[0] + drinks[3] = 2 + 1 = 3;
第 3 种方案:staple[1] + drinks[0] = 1 + 8 = 9;
第 4 种方案:staple[1] + drinks[2] = 1 + 5 = 6;
第 5 种方案:staple[1] + drinks[3] = 1 + 1 = 2;
第 6 种方案:staple[2] + drinks[0] = 1 + 8 = 9;
第 7 种方案:staple[2] + drinks[2] = 1 + 5 = 6;
第 8 种方案:staple[2] + drinks[3] = 1 + 1 = 2;
提示:
1 <= staple.length <= 10^5
1 <= drinks.length <= 10^5
1 <= staple[i],drinks[i] <= 10^5
1 <= x <= 2*10^5
二、解题
1、方法1——暴力法
1.1、思路分析
对于staple中的每个元素,都遍历drinks中的每一个元素,用一个变量count统计符合要求的组合
count初始化为0。当staple[i] + drinks <= x时,令count++,当i == j时跳过。
最后返回count % 1000000007即可。
1.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
int breakfastNumber1(int* staple, int stapleSize, int* drinks, int drinksSize, int x) {
assert(staple && drinks);
int i = 0;
int j = 0;
int count = 0;
for (i = 0; i < stapleSize; i++) {
for (j = 0; j < drinksSize; j++) {
if (i == j) {
continue;
}
if (staple[i] + drinks[j] <= x) {
count++;
}
}
}
return count % 1000000007;
}
时间复杂度:O(nm),其中n和m分别为staple数组和drinks数组的长度。
空间复杂度:O(1),我们只需要用到常数级的额外空间。
(但这个方法时间复杂度过高,提交给LeetCode这样斯文的网站是不能通过的)
2、方法2——二分法
2.1、思路分析
我们其实可以先对两个数组进行升序排序,然后顺序遍历其中一个数组,对于其中的每一个元素,都在另一个数组中使用二分法寻找能与其组合成满足条件的组合的元素个数。
假设我们顺序遍历的数组为staple,那我们就只需要在drinks数组中使用二分法找到第一个大于x - drinks[i]的元素即可,此元素的下标即为drinks数组中小于或等于x - drinks[i]的元素的个数:
我们将其累加起来即可。
而如果drinks数组中的所有元素都小于或等于x - staple[i],那我们直接加上数组drinks的长度即可:
同时在整个过程中由于被顺序遍历的数组中的价格也是不断上升的,因此可选用的饮料的最大索引位置的右边界是不断缩小的,
所以可以采用这个思路来加快整体的运算进程。
2.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
// 先写一个函数,比较两个整型的大小
int cmp_int(const void* p1, const void* p2) {
assert(p1 && p2);
return *((int*)p1) - *((int*)p2);
}
int breakfastNumber2(int* staple, int stapleSize, int* drinks, int drinksSize, int x) {
assert(staple && drinks);
// 先对其中一个数组进行排序
qsort(drinks, drinksSize, sizeof(int), cmp_int);
qsort(staple, stapleSize, sizeof(int), cmp_int);
int i = 0;
int left = 0;
int right = 0;
int mid = 0;
int d = 0;
long long count = 0;
for (i = 0; i < stapleSize; i++) {
left = 0;
right = drinksSize - 1;
d = x - staple[i];
while (left < right) {
if (drinks[left] > d) {
count += left;
break;
}
mid = left + (right - left) / 2;
if (drinks[mid] <= d) {
left = mid + 1;
}
else {
right = mid;
}
}
if (left >= right) {
if (drinks[left] > d) {
count += left;
}
else if (drinks[drinksSize - 1] <= d) {
count += drinksSize;
}
}
}
return count % 1000000007;
}
时间复杂度:O((m+n)log(n)+mlog(m)),其中n和m分别为两个数组的长度。
空间复杂度:O(1),我们只需要用到常数级的额外空间。