菜鸟刷题之路——Q29:加油站问题(贪心算法)

问题:134. 加油站

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

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:

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

分析

  这是贪心算法里相当典型的案例。题目要求的是开一周,即每个点都要经过,那么我们需要计算每个点油量的损耗(gas[i] - cost[i])。在此基础上,我们来看暴力的办法:

  从每个点开始遍历,走一周,每到一个新的点时,就判断当前的油量是否大于等于0:

  • 如果是,则说明可以从出发点到这个节点;
  • 如果不是,则说明无法到达;

  以上的暴力解法是相当好理解的。如何进行加速呢?思考一个过程,现在有ABC三个点,对应我们的遍历过程,如果从A点出发可以到达B点,即油量有剩余,此时从B点出发无法到C点。此时我们以A为起点的遍历过程就结束了认为A不是答案,但是我们一定要从B再开始吗?

  其实不是,可以直接从C点开始,因为如果从A无法到C,那么A—C之间的节点都不可能是答案。为什么呢?因为假设从A可以到B,说明gas[A]-cost[B]>=0, 那么从A出发到C剩余的油量一定大于等于从B出发到C剩余的油量,如果从A都无法到达,从B出发也一定无法到达。此时的时间复杂度为O(n),空间复杂度为O(n)。

Code

/*
执行结果:
通过
显示详情
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.7 MB, 在所有 Java 提交中击败了83.83% 的用户
*/
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int len = gas.length;
        int[] remain = new int[len];
        // 获取每个节点的油量耗损
        for (int i = 0; i < len; i++) remain[i] = gas[i] - cost[i]; 
        for (int i = 0; i < len; i++) {
            int sum = 0, step = 0;     
            while (step < len) {
                int idx = (i+step) % len;
                sum += remain[idx];
                if (sum < 0) {
                    // 下面这个if判断就是64ms和0ms的区别, 即剪枝过程
                    if (i < idx) i = idx;
                    break;
                }
                step++;
            }
            if (sum >= 0) return i;
        }
        return -1;
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值