加油站问题

举报
程序员学长 发表于 2022/03/10 19:03:41 2022/03/10
【摘要】 加油站读前福利,最全pdf获取联系我 问题描述在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。说明:如果题目有解,该答案即为唯一答案。输入数组均为非...

加油站

读前福利,最全pdf获取

联系我

问题描述

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:

  • 如果题目有解,该答案即为唯一答案。
  • 输入数组均为非空数组,且长度相同。
  • 输入数组中的元素均为非负数。

示例:

输入:gas = [1,2,3,4,5],cost = [3,4,5,1,2]

输出:3

解释:从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

分析问题

我们可以把题目的描述抽象成如下的图形结构。其中节点代表每个加油站(节点的值表示可以添加的油量),边的值表示从一个点到另外一个点需要消耗的油量。

image-20211221114216856

这道题最容易想到的就是暴力求解,即遍历所有的站点,以其为出发点,看是否能转一圈再回来。该算法使用了两层for循环,其算法的时间复杂度是O(N^2)。

class Solution:
    def canCompleteCircuit(self, gas, cost):
        #站点个数
        n=len(gas)
        #遍历所有站点,以其为出发点
        for start in range(n):
            j = start
            remain = gas[start]
            #判断当前剩余的油能否到达下一个点
            while remain - cost[j] >= 0:
                #更新剩余的油量
                remain = remain - cost[j] + gas[(j + 1) % n]
                #顺时针移动一位
                j = (j + 1) % n
                #当回到了起始点,代表可以走完一圈,直接返回
                if j == start:
                    return start
        return -1

下面我们来看一下如何进行优化。

优化

在汽车进入加油站站点 i 时,可以加 gas[i] 的油,当离开站点 i ,走到下一个站点 i+1 时, 需要消耗 cost[i] 的油,那么我们可以把站点和与其相连的路看做一个整体来考虑,即 gas[i] - cost[i] 作为经过站点 i 的 油的变化量。如下图所示:

image-20211221140117536

这样,我们就生成了一个环形数组,数组中的第 i 个元素就是 gas[i] - cost[i]。

通过转换后,我们就可以把题目要求变成判断这个环形数组中是否能够找到一个起点start,使得从这个起点开始的累加和一直大于等于 0

在计算累加和的过程中,如果累加和小于0,就代表以该加油站为起点,是不能走完一圈的,需要换一个加油站作为起点。

决定换哪个加油站为起点呢?这是本题优化的关键所在,也是使用贪心思路求解的关键结论。

这个结论就是:如果选择加油站i作为起点「恰好」无法走到加油站j,那么加油站 ij中间的任意站点k都不可能作为起点

下面我们试着来证明一下这个结论的正确性。

假设 sum 记录的是当前油箱的剩余油量,如果从加油站 i (sum=0)出发,走到加油站 j 恰好出现 sum < 0 的情况,也就是说从站点 i 走到 站点k(站点 k 位于 站点 i 和 j 之间)都满足 sum > 0 。

如果把 k 作为起点的话,相当于在站点 k 时, sum=0(出发时,油箱的油量为0),则走到站点 j 时 必然有 sum < 0,也就说明 k 肯定不能是起点。

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

所以,如果我们发现从站点 i 出发无法走到站点 j ,那么站点i以及i, j之间的所有站点都不可能作为起点,从而我们可以减少一些冗余计算了。

具体代码如下所示:

class Solution:
    def canCompleteCircuit(self, gas, cost):
        #站点个数
        n=len(gas)
        #邮箱的剩余量
        sum=0
        #出发位置
        start=0
        while start < n:
            step=0
            while step<n:
                j = (start+step) % n
                # 计算累加和
                sum += gas[j] - cost[j]
                # 如果累加和小于0,
                # 代表以start为起点不能走完一圈
                if sum<0:
                    break

                step=step+1

            if step==n:
                #如果已经走完一圈,直接返回start
                return start
            else:
                #更新start
                start=start+step+1
                sum=0

        return -1
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。