【刷题之路】LeetCode LCP 18. 早餐组合

文章介绍了LeetCode的LCP18问题,即在给定主食和饮料价格及预算限制下,计算购买方案数量。提供了两种解题方法:1)暴力法,通过两层循环遍历所有组合,但时间复杂度过高;2)二分法,对数组排序后,使用二分查找优化求解,降低时间复杂度至O((m+n)log(n)+mlog(m))。
摘要由CSDN通过智能技术生成

一、题目描述

原题连接: 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),我们只需要用到常数级的额外空间。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林先生-1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值