热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

力扣LCP18.早餐组合

篇首语:本文由编程笔记#小编为大家整理,主要介绍了力扣-LCP18.早餐组合相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了力扣 - 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 <&#61; staple.length <&#61; 10^5
1 <&#61; drinks.length <&#61; 10^5
1 <&#61; staple[i],drinks[i] <&#61; 10^5
1 <&#61; x <&#61; 2*10^5

来源&#xff1a;力扣&#xff08;LeetCode&#xff09;
链接&#xff1a;https://leetcode-cn.com/problems/2vYnGI


分析


暴力法

直接双层循环&#xff0c;来遍历就好了

int breakfastNumber(int* staple, int stapleSize, int* drinks, int drinksSize, int x)

int i &#61; 0;
int j &#61; 0;
int count &#61; 0;
for(i &#61; 0; i < stapleSize; i&#43;&#43;)

for(j &#61; 0; j < drinksSize; j&#43;&#43;)

if(x >&#61; staple[i] &#43; drinks[j])

count&#43;&#43;;
count &#61; count % 1000000007;



return count;

很显然&#xff0c;结果超时


排序 &#43; 双指针

其实我们很容易想到先排序&#xff0c;然后再进行查找后遍历。来优化算法。
这里使用C语言库函数qsort&#xff0c;时间复杂度为On*log (n)&#xff0c;我们写的遍历时间复杂度为O(n2)

int compare_int(const void* e1, const void* e2) //比较函数

return *(int*)e1 - *(int*)e2;

int breakfastNumber(int* staple, int stapleSize, int* drinks, int drinksSize, int x)

int i &#61; 0;
int j &#61; 0;
int count &#61; 0;
int start &#61; 0;
int end &#61; 0;
int mid &#61; 0;
// 调用库函数 排序函数
qsort(staple, stapleSize, sizeof(staple[0]), compare_int);
qsort(drinks, drinksSize, sizeof(drinks[0]), compare_int);
for (i &#61; 0; i < stapleSize && staple[i] < x; i&#43;&#43;)

for(j &#61; 0; j < drinksSize && drinks[j] <&#61; x - staple[i]; j&#43;&#43;)

count&#43;&#43;;
count &#61; count % 1000000007;


return count;


居然还超时&#xff0c;我们再想想&#xff0c;这里我们没有考虑到排序后的数据是有序的特性。接下来我们通过二分法继续优化。


排序 &#43; 二分法

我们根据staple的值只需要找到drinks数组中临界值。通过drinks的下标就可以一次知道了&#xff0c;前面都是符合的值&#xff0c;那么我们就可以用二分法来查找第一个不符合的。但是这里还有一个小坑&#xff0c;如果所有数据都成立那么通过二分法得到的也仅仅为下标最大值&#xff0c;所以这里我们可以把所有数据都成立的情况特殊列出来。

这样我们的时间复杂度就是O (log 2 n)与qsort库函数复杂度相同。空间复杂度为1.

int compare_int(const void* e1, const void* e2) //升序排序的比较函数

return *(int*)e1 - *(int*)e2;

int breakfastNumber(int* staple, int stapleSize, int* drinks, int drinksSize, int x)

int i &#61; 0;
int j &#61; 0;
int count &#61; 0;
int start &#61; 0;
int end &#61; 0;
int mid &#61; 0;
// 调用库函数 排序函数
qsort(staple, stapleSize, sizeof(staple[0]), compare_int);
qsort(drinks, drinksSize, sizeof(drinks[0]), compare_int);
for (i &#61; 0; i < stapleSize && staple[i] < x; i&#43;&#43;)

//考虑到最优情况(特殊情况) 所有数据都符合就直接终止
if (drinks[drinksSize - 1] <&#61; x - staple[i])

count &#43;&#61; drinksSize;
continue;

start &#61; 0;
end &#61; drinksSize - 1;
while (start < end)

mid &#61; (start &#43; end) / 2;
if (drinks[mid] <&#61; x - staple[i])

start &#61; mid &#43; 1;

else

end &#61; mid;



count &#43;&#61; start;
count &#61; count % 1000000007;

return count;


功夫不负有心人&#xff0c;终于能跑了。这里我又想到一个点&#xff0c;俩个数组的值都是逐渐增大&#xff0c;而x不变。所以drinks数组的上限end应该小于等于上一个上限


排序 &#43; 优化二分法

俩个数组的值都是逐渐增大&#xff0c;而x不变。所以drinks数组的上限end应该小于等于上一个上限。如果没懂建议多读几遍。所以我们可以再增加一个变量来记录上次的上限用于更新。

int compare_int(const void* e1, const void* e2) //升序排序的比较函数

return *(int*)e1 - *(int*)e2;

int breakfastNumber(int* staple, int stapleSize, int* drinks, int drinksSize, int x)

int i &#61; 0;
int j &#61; 0;
int count &#61; 0;
int start &#61; 0;
int end &#61; 0;
int mid &#61; 0;
int upper &#61; drinksSize - 1; // 用于记录上次所取区间的上限
int len &#61; 0; // 计算相同价格的主食有几种
// 调用库函数 排序函数
qsort(staple, stapleSize, sizeof(staple[0]), compare_int);
qsort(drinks, drinksSize, sizeof(drinks[0]), compare_int);
for (i &#61; 0; i < stapleSize && staple[i] < x; i&#43;&#43;)

// 考虑到最优情况 所有数据都符合就直接终止
if (drinks[drinksSize - 1] <&#61; x - staple[i])

count &#43;&#61; drinksSize;
continue;

// 正常情况
start &#61; 0;
end &#61; upper;
mid &#61; (start &#43; end) / 2;
while (start < end)

if (drinks[mid] <&#61; x - staple[i])

start &#61; mid &#43; 1;

else

end &#61; mid;

mid &#61; (start &#43; end) / 2;

count &#43;&#61; start;
upper &#61; end;
count &#61; count % 1000000007;

return count;


排序 &#43; 二次优化二分法

上面优化了drinks数组&#xff0c;同样是不是可以优化staple数组&#xff1f;是的。我们可以将staple相同的数据统计起来一次计算。这里我们又增加了len一个变量&#xff0c;用来计算staple数组中重复的数据。

int compare_int(const void* e1, const void* e2) //升序排序的比较函数

return *(int*)e1 - *(int*)e2;

int breakfastNumber(int* staple, int stapleSize, int* drinks, int drinksSize, int x)

int i &#61; 0;
int j &#61; 0;
int count &#61; 0;
int start &#61; 0;
int end &#61; 0;
int mid &#61; 0;
int upper &#61; drinksSize - 1; // 用于记录上次所取区间的上限
int len &#61; 0; // 计算相同价格的主食有几种
// 调用库函数 排序函数
qsort(staple, stapleSize, sizeof(staple[0]), compare_int);
qsort(drinks, drinksSize, sizeof(drinks[0]), compare_int);
for (i &#61; 0; i < stapleSize && staple[i] < x; i&#43;&#43;)

len &#61; 1;
while (i < stapleSize - 1 && staple[i] &#61;&#61; staple[i &#43; 1])

i&#43;&#43;;
len&#43;&#43;;

// 考虑到最优情况 所有数据都符合就直接终止
if (drinks[drinksSize - 1] <&#61; x - staple[i])

count &#43;&#61; drinksSize * len;
continue;

// 正常情况
start &#61; 0;
end &#61; upper;
mid &#61; (start &#43; end) / 2;
while (start < end)

if (drinks[mid] <&#61; x - staple[i])

start &#61; mid &#43; 1;

else

end &#61; mid;

mid &#61; (start &#43; end) / 2;

count &#43;&#61; start * len;
upper &#61; end;
count &#61; count % 1000000007;

return count;


空间换时间&#xff0c;复杂度直接降为O(n)

因为早餐的价格均为整数&#xff0c;我们有x的资金。即我们早餐主食的选择有&#xff1a;1 2 3 … x-1。我们可以用一个n[x]的数组来实现&#xff0c;数组中存放的是当前下标价位的早餐种类。一次遍历主食staple数组&#xff0c;获取到符合要求的主食种类。当我们主食价格为2元的搭配都符合时&#xff0c;那么主食为1元的也不是符合吗&#xff1f;
所有我们还需要遍历一次&#xff0c;n[i] &#43;&#61; n[i - 1] &#43; n[i - 2] &#43; ... n[1];
最后我们只需要对drinks数组遍历&#xff0c;n[x - drinks[i]]里面存放的值就是当前饮料对应主食的种类。
让我们实现吧。

int breakfastNumber(int* staple, int stapleSize, int* drinks, int drinksSize, int x)

int n[x];
int i &#61; 0;
int count &#61; 0;
memset(n, 0, sizeof(int) * x);
// 将主食价格小于x的放入数组对应的下标&#xff0c;下标里面放的数字表示当前下标出现的次数
for(i &#61; 0; i < stapleSize; i&#43;&#43;)

if(staple[i] < x)//主食价格小于总金额

n[staple[i]]&#43;&#43;;


// 因为前面的价格必然小于等于后面的价格&#xff0c;也就是只要符合当前下标的话&#xff0c;你前面也都符合&#xff0c;
// 所以我们直接累加。假如现在主食8元。n[8]中放的是主食1...8的所有种数
for(i &#61; 2; i < x; i&#43;&#43;)

n[i] &#43;&#61; n[i - 1];

for(i &#61; 0; i < drinksSize; i&#43;&#43;)

if(x - drinks[i] > 0)

count &#43;&#61; n[x - drinks[i]];
count &#61; count % 1000000007;


return count;


这样我们的时间复杂度只有O(n),空复杂度也为O(n)

本章完&#xff01;







推荐阅读
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • C语言判断正整数能否被整除的程序
    本文介绍了使用C语言编写的判断正整数能否被整除的程序,包括输入一个三位正整数,判断是否能被3整除且至少包含数字3的方法。同时还介绍了使用qsort函数进行快速排序的算法。 ... [详细]
  • C语言常量与变量的深入理解及其影响
    本文深入讲解了C语言中常量与变量的概念及其深入实质,强调了对常量和变量的理解对于学习指针等后续内容的重要性。详细介绍了常量的分类和特点,以及变量的定义和分类。同时指出了常量和变量在程序中的作用及其对内存空间的影响,类似于const关键字的只读属性。此外,还提及了常量和变量在实际应用中可能出现的问题,如段错误和野指针。 ... [详细]
  • c语言基础编写,c语言 基础
    本文目录一览:1、C语言如何编写?2、如何编写 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
author-avatar
火鸟大叔
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有