134. Gas Station

Problem:

There is a list of gas stations connected in a circular route. Each gas station has gas[i] amount of fuel, and it requires cost[i] to move to the next station. Print the index of the starting point from where it's possible to visit all gas stations without running out of fuel. If no such starting point exists, return -1. The starting point, if it exists, is unique.

Example 1:

Input: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
Output: 3
Explanation:
Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 4. Your tank = 4 - 1 + 5 = 8
Travel to station 0. Your tank = 8 - 2 + 1 = 7
Travel to station 1. Your tank = 7 - 3 + 2 = 6
Travel to station 2. Your tank = 6 - 4 + 3 = 5
Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3.
Therefore, return 3 as the starting index.

Example 2:

Input: gas = [2,3,4], cost = [3,4,3]
Output: -1
Explanation:
You can't start at station 0 or 1, as there is not enough gas to travel to the next station.
Let's start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 0. Your tank = 4 - 3 + 2 = 3
Travel to station 1. Your tank = 3 - 3 + 3 = 3
You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3.
Therefore, you can't travel around the circuit once no matter where you start.

https://leetcode.com/problems/gas-station/

What to learn:

Shift in thinking

Since the route of the gas stations is circularly connected, the indices can be reset from 0 using modulo operations. However, this approach results in a time complexity of O(n2)O(n^2).

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        for start in range(len(gas)):
            fuel = 0
            for i in range(start, len(gas) + start):
                index = i % len(gas)
                
                can_travel = True
                if gas[index] + fuel < cost[index]:
                    can_travel = False
                    break
                else:
                    fuel += gas[index] - cost[index]
            if can_travel:
                return start
        return -1
        

If the total amount of gas is greater than the total cost, there must exist a starting point that can visit all stations. Given the constraint that the starting point is unique, there can only be one such point. Therefore, while visiting all stations, if a point is found not to be feasible as a starting point, the search for the starting point is moved back one station. If a point is found not to be feasible, then all points before it cannot be the starting point either.

Similar to proof by contradiction in mathematics, the starting point can be found by excluding points that are not feasible

Solution:

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        if sum(gas) < sum(cost):
            return -1
        
        start, fuel = 0, 0
        for i in range(len(gas)):
            if gas[i] + fuel < cost[i]
                start = i + 1
                fuel = 0
            else:
                fuel += gas[i] - cost[i]
        return start        
Let's consider the example step by step:
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

Let's break down the flow:

  1. First, we check if the total gas is less than the total cost. It's not, so we proceed.

  2. We initialize start as 0 and fuel as 0.

  3. Iteration 1 (i=0):

    • At station 0: gas = 1 and fuel = 0. To get to the next station, we need cost[0] = 3 units of gas. We don't have enough gas.

    • So, we set start = i + 1 = 1 and reset fuel = 0.

  4. Iteration 2 (i=1):

    • At station 1: gas = 2 and fuel = 0. To get to the next station, we need cost[1] = 4 units of gas. Again, not enough.

    • So, we set start = i + 1 = 2 and reset fuel = 0.

  5. Iteration 3 (i=2):

    • At station 2: gas = 3 and fuel = 0. To get to the next station, we need cost[2] = 5. Again, we don't have enough.

    • So, we set start = i + 1 = 3 and reset fuel = 0.

  6. Iteration 4 (i=3):

    • At station 3: gas = 4 and fuel = 0. To get to the next station, we need cost[3] = 1 unit of gas. We have enough gas.

    • So, we update our fuel: fuel = fuel + gas[i] - cost[i] = 4 - 1 = 3 units of gas left.

    • We don't update the start, it remains at 3.

  7. Iteration 5 (i=4):

    • At station 4: gas = 5 and fuel = 3 from previous station. To get to the next station, we need cost[4] = 2 units of gas. We have a total of 5 + 3 = 8 units of gas, which is more than enough.

    • So, we update our fuel again: fuel = fuel + gas[i] - cost[i] = 3 + 5 - 2 = 6 units of gas left.

    • We don't update the start, it remains at 3.

The loop ends here. You may wonder, what about moving from station 4 back to station 0 and then 1 and 2?

Here's the clever part: We've already checked and found that starting from stations 0, 1, and 2 wasn't viable. Station 3 was the first station from where we could move forward without running out of gas till the end (station 4). And since we have a positive amount of fuel left when we reach the end and the total gas across all stations is more than the total cost, we're guaranteed to complete the circle when we start from station 3.

Hence, the answer is 3. The algorithm doesn't need to actually simulate the entire journey. The combination of checking the total gas vs cost and finding the first viable starting station is enough to ensure the correctness of the solution.

Last updated