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

    文章

    6511

    浏览量

    87590
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66746

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

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

收藏 人收藏

    评论

    相关推荐

    请问STM32 USB通信延迟有多大?

    STM32作为从机,接收来自PC的实时IO信号,大约每秒100次,那每次信号只有20ms的间隔我用串口做,主机发送到从机接收到,延迟很不稳定,大约从10ms-40ms,从机执行的IO信号的时候很不均匀。。 不知道USB通信能做到多低的延迟,会不会出现串口的问题 不知道还有别的解法没? 谢谢各位大佬!
    发表于 04-18 08:26

    这才是正规的电线接头解法

    每根电线本身强度需小于接头机械强度;主线需小于接头绝缘性能;相连接的电线不能小于接头电阻;电线接头的耐氧化性能和耐腐蚀要好。总的来说,这4点是电线接头需要做好的规范。
    发表于 03-04 10:41 159次阅读
    这才是正规的电线接头<b class='flag-5'>解法</b>!

    LTM80535输出电流稍微大一点输出电压就减小是为什么?

    LTM80535 输出电流稍微大一点 输出电压就减小 外部电路解法正确 是电源模块有故障吗 而且电压降低后有啸叫声 不确定是不是模块内部的电感引起的
    发表于 01-04 06:40

    再登Nature!DeepMind大模型突破60年数学难题,解法超出人类已有认知

    用大模型解决困扰数学家60多年的问题,谷歌DeepMind最新成果再登 Nature。 作者之一、谷歌DeepMind研究副总裁Pushmeet Kohli表示: 训练数据中不会有这个方案,它之前
    的头像 发表于 12-24 21:40 336次阅读
    再登Nature!DeepMind大模型突破60年<b class='flag-5'>数学</b>难题,<b class='flag-5'>解法</b>超出人类已有认知

    求助,基于OP1177的差分放大电路的CMRR求教

    在OP1177的DATASHEET,可见其中的一种差分放大电路的应用: 图1差分放大电路 这是一种比较基本的差分放大电路,DATASHEET中分析了其CMRR的大小,方法如下: 图2 CMRR的求解 图2所示为DATASHEET
    发表于 11-20 07:34

    含受控源电路变换控制量解法初探

    电子发烧友网站提供《含受控源电路变换控制量解法初探.pdf》资料免费下载
    发表于 11-18 15:11 0次下载
    含受控源电路变换控制量<b class='flag-5'>解法</b>初探

    曼恩斯特锂电极片智造“新解法

    新能源汽车渗透率越过30%市场拐点,叠加全球储能市场发展带动锂电池需求增长,锂电设备及核心部件需求也进一步增长。
    的头像 发表于 11-17 10:16 362次阅读

    MATLAB数学建模编程资料

    它已经成为世界上应用最广泛的数学软件之一,尤其在工程计算领域、高校应用最广。该软件以矩阵运算为基础,将计算、可视化、程序设计融合在简单易用的交互式环境。u3000u3000运用MATLAB可以实现
    发表于 09-22 08:19

    H3CNE综合小实验解法

    注:如无特别说明,描述中的 R1 或 SW1 对应拓扑中设备名称末尾数字为 1 的设备,R2 或 SW2 对应拓扑中设备名称末尾数字为 2 的设备,以此类推;另外,同一网段中,IP 地址的主机位为其设备编号,如 R3 的 g0/0 接口若在192.168.1.0/24[1]网段,则其 IP 地址为192.168.1.3/24[2],以此类推。
    的头像 发表于 09-14 09:42 402次阅读
    H3CNE综合小实验<b class='flag-5'>解法</b>

    全覆盖路径规划算法(CCPP)工作原理解析

    根据CCPP算法工作原理不同,可以分为随机碰撞法、单元分解法、生物激励法、模板法、智能算法等,但CCPP算法都应该满足覆盖必须满足的要求。
    发表于 08-25 10:31 911次阅读
    全覆盖路径规划算法(CCPP)工作原理解析

    李毅中:我国工业制造业存在的9个问题及解法

    世界经济复苏迟缓,国际经济组织预测今年全球经济只增长2.7%,其中美国1.6%,我国经济持续恢复,但是目前的数据来看,弱于预期,下行的压力有所加大,恢复的基础还不牢固。发展经济的着力点是实体经济,工业和制造业是根基。
    的头像 发表于 08-08 16:01 417次阅读

    AD19 PCB 走線 一直run DRC

    AD19 PCB 走線 一直run DRC 每執行一個指令,就卡在那裡 一直跑畫面 請問有方法解法嗎? 謝謝
    发表于 08-07 13:13

    在LPCXpresso v4.3.0调试我的代码时,下载图像后显示错误怎么解决?

    当我在 LPCXpresso v4.3.0 调试我的代码时,然后在下载图像后显示错误 Quote: 15: Set break/watch 的目标错误 无法设置执行中断 - 没有可用资源。 我没有在我的
    发表于 06-09 08:15

    PLC的五大编程技术方法

    PLC 的编程方法大体上有 5 种:经验法、解析法、图解法、技巧法及计算机辅助设计法。
    的头像 发表于 05-15 10:25 4330次阅读

    c++常见函数集

    c++常见函数集包括:线性代数方程组的解法、插值、数值积分、特殊函数、函数逼近、随机数排序、特征值问题、数据拟合、方程求根和非线性方程组的解法、函数的极值和最优、傅里叶变换谱方法、数据的统计描述等
    发表于 05-09 14:52 1次下载