0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

代码中是数学图像解法和贪心解法

新材料在线 来源:labuladong 作者:labuladong 2021-09-01 14:14 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

今天讲一个贪心的老司机的故事,就是力扣第 134 题「加油站」:

5fda8eb2-fbbe-11eb-9bcf-12bb97331649.jpg

题目应该不难理解,就是每到达一个站点i,可以加gas[i]升油,但离开站点i需要消耗cost[i]升油,问你从哪个站点出发,可以兜一圈回来。

要说暴力解法,肯定很容易想到,用一个 for 循环遍历所有站点,假设为起点,然后再套一层 for 循环,判断一下是否能够转一圈回到起点:

int n = gas.length;

for (int start = 0; start 《 n; start++) {

for (int step = 0; step 《 n; step++) {

int i = (start + step) % n;

tank += gas[i];

tank -= cost[i];

// 判断油箱中的油是否耗尽

}

}

很明显时间复杂度是 O(N^2),这么简单粗暴的解法一定不是最优的,我们试图分析一下是否有优化的余地。

暴力解法是否有重复计算的部分?是否可以抽象出「状态」,是否对同一个「状态」重复计算了多次?

我们前文 动态规划详解 说过,变化的量就是「状态」。那么观察这个暴力穷举的过程,变化的量有两个,分别是「起点」和「当前油箱的油量」,但这两个状态的组合肯定有不下 O(N^2) 种,显然没有任何优化的空间。

所以说这道题肯定不是通过简单的剪枝来优化暴力解法的效率,而是需要我们发现一些隐藏较深的规律,从而减少一些冗余的计算。

下面我们介绍两种方法巧解这道题,分别是数学图像解法和贪心解法。

图像解法

汽车进入站点i可以加gas[i]的油,离开站点会损耗cost[i]的油,那么可以把站点和与其相连的路看做一个整体,将gas[i] - cost[i]作为经过站点i的油量变化值:

这样,题目描述的场景就被抽象成了一个环形数组,数组中的第i个元素就是gas[i] - cost[i]。

有了这个环形数组,我们需要判断这个环形数组中是否能够找到一个起点start,使得从这个起点开始的累加和一直大于等于 0。

如何判断是否存在这样一个起点start?又如何计算这个起点start的值呢?

我们不妨就把 0 作为起点,计算累加和的代码非常简单:

int n = gas.length, sum = 0;

for (int i = 0; i 《 n; i++) {

// 计算累加和

sum += gas[i] - cost[i];

}

sum就相当于是油箱中油量的变化,上述代码中sum的变化过程可能是这样的:

显然,上图将 0 作为起点肯定是不行的,因为sum在变化的过程中小于 0 了,不符合我们「累加和一直大于等于 0」的要求。

那如果 0 不能作为起点,谁可以作为起点呢?

看图说话,图像的最低点最有可能可以作为起点:

如果把这个「最低点」作为起点,就是说将这个点作为坐标轴原点,就相当于把图像「最大限度」向上平移了。

再加上这个数组是环形数组,最低点左侧的图像可以接到图像的最右侧:

这样,整个图像都保持在 x 轴以上,所以这个最低点 4,就是题目要求我们找的起点。

不过,经过平移后图像一定全部在 x 轴以上吗?不一定,因为还有无解的情况:

如果sum(gas[。..]) 《 sum(cost[。..]),总油量小于总的消耗,那肯定是没办法环游所有站点的。

综上,我们就可以写出代码:

int canCompleteCircuit(int[] gas, int[] cost) {

int n = gas.length;

// 相当于图像中的坐标点和最低点

int sum = 0, minSum = Integer.MAX_VALUE;

int start = 0;

for (int i = 0; i 《 n; i++) {

sum += gas[i] - cost[i];

if (sum 《 minSum) {

// 经过第 i 个站点后,使 sum 到达新低

// 所以站点 i + 1 就是最低点(起点)

start = i + 1;

minSum = sum;

}

}

if (sum 《 0) {

// 总油量小于总的消耗,无解

return -1;

}

// 环形数组特性

return start == n ? 0 : start;

}

以上是观察函数图像得出的解法,时间复杂度为 O(N),比暴力解法的效率高很多。

下面我们介绍一种使用贪心思路写出的解法,和上面这个解法比较相似,不过分析过程不尽相同。

贪心解法

用贪心思路解决这道题的关键在于以下这个结论:

如果选择站点i作为起点「恰好」无法走到站点j,那么i和j中间的任意站点k都不可能作为起点。

比如说,如果从站点1出发,走到站点5时油箱中的油量「恰好」减到了负数,那么说明站点1「恰好」无法到达站点5;那么你从站点2,3,4任意一个站点出发都无法到达5,因为到达站点5时油箱的油量也必然被减到负数。

如何证明这个结论?

假设tank记录当前油箱中的油量,如果从站点i出发(tank = 0),走到j时恰好出现tank 《 0的情况,那说明走到i, j之间的任意站点k时都满足tank 》 0,对吧。

如果把k作为起点的话,相当于在站点k时tank = 0,那走到j时必然有tank 《 0,也就是说k肯定不能是起点。

拜托,从i出发走到k好歹tank 》 0,都无法达到j,现在你还让tank = 0了,那更不可能走到j了对吧。

综上,这个结论就被证明了。

回想一下我们开头说的暴力解法是怎么做的?

如果我发现从i出发无法走到j,那么显然i不可能是起点。

现在,我们发现了一个新规律,可以推导出什么?

如果我发现从i出发无法走到j,那么i以及i, j之间的所有站点都不可能作为起点。

看到冗余计算了吗?看到优化的点了吗?

这就是贪心思路的本质,如果找不到重复计算,那就通过问题中一些隐藏较深的规律,来减少冗余计算。

根据这个结论,就可以写出如下代码:

int canCompleteCircuit(int[] gas, int[] cost) {

int n = gas.length;

int sum = 0;

for (int i = 0; i 《 n; i++) {

sum += gas[i] - cost[i];

}

if (sum 《 0) {

// 总油量小于总的消耗,无解

return -1;

}

// 记录油箱中的油量

int tank = 0;

// 记录起点

int start = 0;

for (int i = 0; i 《 n; i++) {

tank += gas[i] - cost[i];

if (tank 《 0) {

// 无法从 start 走到 i

// 所以站点 i + 1 应该是起点

tank = 0;

start = i + 1;

}

}

return start == n ? 0 : start;

}

这个解法的时间复杂度也是 O(N),和之前图像法的解题思路有所不同,但代码非常类似。

其实,你可以把这个解法的思路结合图像来思考,可以发现它们本质上是一样的,只是理解方式不同而已。

对于这种贪心算法,没有特别套路化的思维框架,主要还是靠多做题多思考,将题目的场景进行抽象的联想,找出隐藏其中的规律,从而减少计算量,进行效率优化。

好了,这道题就讲到这里,希望对你拓宽思路有帮助。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    7348

    浏览量

    95017
  • 代码
    +关注

    关注

    30

    文章

    4976

    浏览量

    74378

原文标题:当老司机学会了贪心算法

文章出处:【微信号:xincailiaozaixian,微信公众号:新材料在线】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    云端协同,效率翻倍——智慧园区变电所运维新解法

    1、概述   变电所运维云平台可以看做是电力监控系统的网络应用延伸,变电所运维云平台通过互联网,电力运维人员通过手机可以随时随地了解园区配电系统的运行情况,做到无人值守或者少人值守,同时可以监测用能状况、漏电、线缆异常发热等,并可以通过APP下发派工任务,闭环消缺流程,及时排除隐患。 2、参考标准 DL/T 5430    《无人值班变电站远方监控中心设计技术规程》  程瑜    187 0211 2087 GB/T 2887    《计算机场地通用规范》 Q/GDW 231 
    的头像 发表于 01-22 13:40 268次阅读
    云端协同,效率翻倍——智慧园区变电所运维新<b class='flag-5'>解法</b>

    面对AI降维打击,陈天桥的全新解法:人类必须“肉身进化”

    “坚船利炮已经出现在地平线上,而我们还坚持用大刀长矛守港口。” 这是陈天桥在最新文章对当前人类处境的惊人比喻。这里的“坚船利炮”,指的是正在爆发的人工智能。与主流观点担忧AI抢走工作不同,陈天桥
    的头像 发表于 01-16 17:11 1067次阅读

    深演智能入选艾瑞咨询2025年国企业级AI应用行业研究报告

    深演智能荣誉入选艾瑞咨询发布的《2025年国企业级AI应用行业研究报告》,并携手客户遇见小面展示了 AI 助力连锁餐饮品牌的新解法,彰显了其在中国企业级 AI 决策领域的领先地位。
    的头像 发表于 01-14 11:30 879次阅读

    损耗↓15%!AI服务器电源变压器解法新思路

    千赫兹甚至接近兆赫兹区间。 在这一过程,变压器产品所承受的工作条件已明显不同于传统服务器时代。高频化带来的损耗放大、散热受限以及寄生参数问题,使变压器产品逐渐成为制约电源系统进一步优化的重要变量。 在这样的技术环境下,变压器
    的头像 发表于 01-07 13:54 479次阅读
    损耗↓15%!AI服务器电源变压器<b class='flag-5'>解法</b>新思路

    【乾芯QXS320F开发板试用】基于数学库IQmath定点库的调试

    += 32768; } -> 这是为了处理第二、三象限的角度,因为atan的范围是**[-π/2, π/2],需要加上π(在IQ15是32768)来扩展到[-π, π]。 总结: 这段代码的核心数学
    发表于 12-08 22:48

    无万卡,不VLA:元戎启行与阿里云的“想法”和“解法

    1980年代,卡内基梅隆大学的机器人专家汉斯·莫拉维克(Hans Moravec)提出人工智能领域的一个悖论:让计算机在逻辑推理、下棋等高级智力活动达到甚至超越人类水平,相对容易;但要让它具备孩童
    的头像 发表于 11-12 09:28 565次阅读
    无万卡,不VLA:元戎启行与阿里云的“想法”和“<b class='flag-5'>解法</b>”

    上新 | 有限空间“新解法”!凌科LP20系列90°工业级连接器新品上市

    LP20系列90°连接器新品紧凑布线有了新选择,90°结构让空间局限有了“新解法”。凌科LP20系列90°工业级连接器全新上线,为有限空间布线和转角布线带来全新连接解决方案。LP20系列90°连接器
    的头像 发表于 11-04 18:12 648次阅读
    上新 | 有限空间“新<b class='flag-5'>解法</b>”!凌科LP20系列90°工业级连接器新品上市

    淘宝拍立淘接口实战:图像优化、识别调优与避坑代码示例

    本文详解淘宝拍立淘接口(taobao.picture.search)实战技巧,涵盖图像预处理、识别优化、签名生成与供应链数据联动,结合代码示例解析高频坑点,如Base64格式错误、限流处理、分页失效等,助开发者提升识别率至85%以上,高效对接电商选品与供应链系统。
    的头像 发表于 10-09 14:28 918次阅读

    Nuvoton_8051_ISP-ICP_Programmer_v7.15缺少ISP加载器的源代码怎么解决?

    下载 nuvoton_8051_isp-icp_programmer_v7.15.zip 解压缩为多个 (~ 44) 加载器图像文件,在目录 (3) 新唐标准ISP代码 但这些只是二进制的 例如
    发表于 08-18 06:30

    鸿蒙非侵入式弹窗新解法,企查查正式开源“QuickDialog”弹窗组件库

    、可复用的设计理念,有望成为鸿蒙应用开发管理复杂弹窗场景的最佳实践方案之一。 在当前鸿蒙应用开发,弹窗能力主要依赖 @CustomDialog 或 promptAction.openCustomDialog 等方案实现。这些方式虽然基础能力齐全,但自定义弹窗的生命周期
    的头像 发表于 07-31 10:40 846次阅读
    鸿蒙非侵入式弹窗新<b class='flag-5'>解法</b>,企查查正式开源“QuickDialog”弹窗组件库

    炭黑含量测试仪在色母粒的应用

    ,因此,精准测定炭黑含量极为关键。​炭黑含量测试仪多采用高温分解法,即在氮气等惰性气体保护下,将含有炭黑的色母粒试样置于高温环境。此时,色母粒的有机基体,如塑料
    的头像 发表于 07-23 10:32 526次阅读
    炭黑含量测试仪在色母粒<b class='flag-5'>中</b>的应用

    【微五科技CF5010RBT60开发板试用体验】Cordic数学计算测试

    point in type e and f */ 支持的运算如下,数据都是放大了32768,即左移15位。 测试代码如下计算sin,硬件计算和math库计算对比驱动未完善,这里补充sin的计算
    发表于 06-27 23:55

    戴尔PowerEdge R7715服务器性能评测

    数据中心越来越“卷”,服务器也要“十八般武艺”,核心要多,内存要快,接口要新,功耗还得低?这听起来像“不可能三角”,但戴尔PowerEdge R7715,用一台机架,给出一体解法
    的头像 发表于 06-26 17:48 1918次阅读
    戴尔PowerEdge R7715服务器性能评测

    基于LockAI视觉识别模块:C++使用图像的统计信息

    )。 COLOR_BGR2GRAY:将BGR图像转换为灰度图像。 返回值: 无。最后结果储存在grayImage。注意: 其中根据不同的转换要求可以使用不同的转换代码,具体如下所示
    发表于 05-08 10:31

    技术分享 | 高逼真合成数据助力智驾“看得更准、学得更快”

    自动驾驶研发如何高效获取海量训练数据?高逼真合成数据技术正在提供新解法。通过仿真平台可生成多场景、多传感器的精准标注数据。文章详解如何构建符合nuScenes标准的数据集,覆盖复杂交通场景,为感知模型训练提供高效、可控的数据支持。
    的头像 发表于 04-29 10:47 3651次阅读
    技术分享 | 高逼真合成数据助力智驾“看得更准、学得更快”