00贪心算法中等 LeetCode134. 加油站 NC235 加油站

134. 加油站

描述

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

分析

暴力方法:让每个加油站作为起始结点,遍历一遍,直到找到能够走通全部结点的加油站。时间复杂度是O(n^2)。
for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while!
暴力解法的思路:遍历整个数组,若从i出发然后能够回到i,说明i是起始加油站。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        for(int i = 0; i < n; i++){
            int cur = gas[i];
            int index = i;
            while(cur >= cost[index]){
                cur = cur - cost[index] + gas[(index + 1)%n];
                index = (index + 1)%n;
                //在while循环内判断,若能够跑一圈,则这个while循环会一直循环下去,所以需要在while内部判断,结束循环。
                if(index == i){
                    return i;
                }
            }
        }
        return -1;
    }
}

暴力算法的优化:若从i出发最远走到j,则[i~j]之间的加油站都不可能走一圈,最远走到j。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        for(int i = 0; i < n; i++){
            int cur = gas[i];
            int index = i;
            while(cur >= cost[index]){
                cur = cur - cost[index] + gas[(index + 1)%n];
                index = (index + 1)%n;
                if(index == i){
                    return i;
                }
            }
            if(index < i){
                return -1;
            }
            i = index;
        }
        return -1;
    }
}

2022-02-05

import java.util.*;
public class Solution {
    public int gasStation (int[] gas, int[] cost) {
        for(int i = 0; i < gas.length; i++){
            if(gas[i] < cost[i]){
                continue;
            }
            int reminds = 0;
            int j = i;
            while(j != i + gas.length){
                reminds += gas[j%gas.length] - cost[j%gas.length];
                if(reminds < 0){
                    break;
                }
                j++;
            }
            if(reminds >= 0){
                return i;
            }
            i = --j;
        }
        return -1;
    }
}

贪心算法:计算每个加油站减去消耗后剩余的存量,遍历这些存量,这些存量的和若大于等于0,则能跑一圈,否则不能。
第一步:判断是否能够跑一圈
第二步:能跑一圈则找到起始加油站
起始加油站一定满足:从该加油站的剩余油量开始累加,累加和一直不会小于0,即总是有足够的油量跑到下一站。关键是剩余油量的累加和。操作步骤:

  1. 计算每一站的剩余油量
  2. 累加每一站的剩余油量,和若小于0,说明前面不存在起始加油站,则从下一站开始重新累加,直到遍历结束。如存在一个加油站A,从A开始到最后一个加油站剩余油量的累加和大于0,则该加油站A就是起始结点。

证明:

  • 因为能跑一圈,所以一定存在起始加油站;
  • A前面的加油站剩余油量累加和不能保持大于0,说明从前面的任何一个加油站出发都无法一直走下去。
  • 假设A之后的加油站存在一个加油站B,从B出发能够跑一圈。既然从A开始到最后一个加油站剩余油量的累加和大于0,即说明从A出发能够到达A后面的任意一个加油站,所以从B出发能跑一圈,而A又能到达B,所以A也可以是起始加油站。
  • 假设A之后的加油站不存在一个加油站B,从B出发能够跑一圈。因为一定存在一个加油站从其出发跑一圈,而A前后都没有一个结点连续跑,所以起始加油站一定是A。
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int[] remind = new int[gas.length];
        int sum = 0;
        for(int i = 0; i < gas.length; i++){
            remind[i] = gas[i] - cost[i];
            sum += remind[i];
        }
        if(sum < 0){
            return -1;
        }
        int sumtmp = 0;
        int index = 0;
        for(int i = 0; i < gas.length; i++){
            sumtmp += remind[i];
            if(sumtmp < 0){
                sumtmp = 0;
                index = i + 1;
            }
        }
        return index;
    }
}
import java.util.*;
public class Solution {
    public int gasStation (int[] gas, int[] cost) {
        int n = gas.length;
        int[] reminds = new int[n];
        int sum = 0;
        for(int i = 0; i < n; i++){
            reminds[i] = gas[i] - cost[i];
            sum += reminds[i];
        }
        if(sum < 0){
            return -1;
        }
        
        for(int i = 0; i < n; i++){
            int accumulation = reminds[i];
            if(accumulation < 0){
                continue;
            }
            int j = i++;
            while(i < n && accumulation >= 0){
                accumulation += reminds[i++];
            }
            if(i == n){
                return j;
            }
        }
        return -1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值