# Gas station, how can you be greedy?

Posted by idire on Tue, 23 Nov 2021 16:39:59 +0100

This problem is a little difficult greed.

# 134. Gas stations

Link to force buckle topic: https://leetcode-cn.com/problems/gas-station

There are N gas stations on a ring road, of which the ith gas station has gasoline gas[i] liters.

You have a car with unlimited fuel tank capacity. It takes cost[i] liters of gasoline to drive from the ith gas station to the I + 1st gas station. You start from one of the gas stations and start with an empty tank.

If you can drive around the loop, return to the number of the gas station at the time of departure, otherwise return to - 1.

explain:

• If the question has a solution, the answer is the only answer.
• The input arrays are all non empty arrays with the same length.
• All elements in the input array are non negative numbers.

Example 1: input:

• gas = [1,2,3,4,5]
• cost = [3,4,5,1,2]

Output: 3, interpretation:

• 4 litres of gasoline can be obtained from gas station 3 (index 3). The tank now has = 0 + 4 = 4 litres of petrol
• Drive to No. 4 gas station and there is 4 - 1 + 5 = 8 litres of gasoline in the tank
• Drive to No. 0 gas station and there is 8 - 2 + 1 = 7 litres of gasoline in the tank
• Drive to No. 1 gas station and there is 7 - 3 + 2 = 6 litres of gasoline in the tank
• Drive to gas station 2 and there is 6 - 4 + 3 = 5 litres of gasoline in the tank
• You need to consume 5 liters of gasoline to drive to gas station 3, which is just enough for you to return to gas station 3.
• Therefore, 3 can be the starting index.

Example 2: input:

• gas = [2,3,4]
• cost = [3,4,3]

Output: - 1, interpretation:

You can't start from No. 0 or No. 1 gas station because there isn't enough gas for you to drive to the next gas station. We can get 4 liters of gasoline from gas station 2. The tank now has = 0 + 4 = 4 litres of petrol. Drive to No. 0 gas station and there is 4 - 3 + 2 = 3 litres of gasoline in the tank. Drive to No. 1 gas station and there is 3 - 3 + 3 = 3 litres of gasoline in the tank. You can't return to gas station 2 because you need 4 litres of gasoline on the return trip, but your tank only has 3 litres of gasoline. Therefore, you can't drive around the loop anyway.

## Violent methods

The method of violence is obviously O(n^2). Traverse each gas station as the starting point and simulate a circle.

If the fuel is not cut off during one lap, and the final fuel volume is greater than or equal to 0, it indicates that the starting point is ok.

The method of violence is simple, but the code is not easy to write. The key is to simulate the process of running a lap.

The for loop is suitable for simulating traversal from beginning to end, while the while loop is suitable for simulating loop traversal. Be good at using while!

The C + + code is as follows:

class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
for (int i = 0; i < cost.size(); i++) {
int rest = gas[i] - cost[i]; // Record the remaining oil
int index = (i + 1) % cost.size();
while (rest > 0 && index != i) { // Simulate a lap starting from i
rest += gas[index] - cost[index];
index = (index + 1) % cost.size();
}
// If i is taken as the starting point and the remaining fuel is > = 0, return to the starting position
if (rest >= 0 && index == i) return i;
}
return -1;
}
};
• Time complexity O(n^2)
• Spatial complexity O(n)

C + + can also be submitted on leetcode.

## Greedy algorithm (method 1)

Greedy selection is made directly from the global, as follows:

• Case 1: if the sum of gas is less than the sum of cost, you can't run a lap no matter where you start
• Case 2: rest[i] = gas[i]-cost[i] is the remaining oil of the day. I is calculated from 0 and accumulated to the last station. If there is no negative number in the accumulation, it means that the oil has not been cut off from 0, so 0 is the starting point.
• Case 3: if the minimum accumulated value is negative, the car should start from the non-0 node. From the back to the front, see which node can fill in the negative number. The node that can fill in the negative number is the starting node.

The C + + code is as follows:

class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int curSum = 0;
int min = INT_MAX; // Starting from the starting point, the minimum amount of oil in the tank
for (int i = 0; i < gas.size(); i++) {
int rest = gas[i] - cost[i];
curSum += rest;
if (curSum < min) {
min = curSum;
}
}
if (curSum < 0) return -1;  // Case 1
if (min >= 0) return 0;     // Case 2
// Case 3
for (int i = gas.size() - 1; i >= 0; i--) {
int rest = gas[i] - cost[i];
min += rest;
if (min >= 0) {
return i;
}
}
return -1;
}
};
• Time complexity: O(n)
• Space complexity: O(1)

In fact, I don't think this method is greedy algorithm, because it doesn't find the local optimum, but directly think about the problem from the perspective of global optimum.

But this solution can not tell what method it is. This is a simulation operation to select the optimal solution from the global point of view.

So for this solution is greedy, I have reservations!

But in any case, the solution is ingenious after all, and there is no need to be too persistent in its name.

## Greedy algorithm (method 2)

You can change your mind. First, if the total fuel minus the total consumption is greater than or equal to zero, you can run a lap, indicating that the rest[i] sum of the remaining fuel at the gas station at each station must be greater than or equal to zero.

The rest[i] of each gas station is gas[i] - cost[i].

I accumulates rest[i] from 0, and the sum is recorded as curSum. Once curSum is less than zero, it means that [0, i] interval cannot be used as the starting position. The starting position is calculated from i+1, and then curSum is calculated from 0.

As shown in the figure:

So why once the sum of [i, J] intervals is negative, the starting position can be j+1, and there will be no greater negative number after j+1?

If there is a larger negative number, that is, update J, then the starting position becomes the new j+1.

Moreover, how many negative numbers appear before j and how many positive numbers appear after j, because the total fuel consumption is greater than zero (provided that we have determined that we can finish the whole journey).

Then local optimization: once the sum curSum of the current cumulative rest[j] is less than 0, the starting position must be at least j+1, because it must not start from J. Global Optimization: find the starting position where you can run a lap.

Local optimization can be introduced into global optimization. If you can't find a counterexample, try greed!

The C + + code is as follows:

class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int curSum = 0;
int totalSum = 0;
int start = 0;
for (int i = 0; i < gas.size(); i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0) {   // Once the current cumulative rest[i] and curSum are less than 0
start = i + 1;  // The starting position is updated to i+1
curSum = 0;     // curSum starts at 0
}
}
if (totalSum < 0) return -1; // It means you can't run a lap
return start;
}
};
• Time complexity: O(n)
• Space complexity: O(1)

It is reasonable to say that this solution is a greedy algorithm, because the global optimal solution is derived from the local optimal solution.

## summary

For this problem, we first give the violent solution. The violent solution simulates the process of running a circle. In fact, it tests the code skills. You should be very skilled in using while.

Then two greedy algorithms are given. For the first greedy method, in fact, I think it is a kind of simulation operation that directly selects the optimal operation from the global situation. The idea is still ingenious and worth learning.

For the second greedy method, it really reflects the essence of greedy. Using local optimization, we can deduce global optimization, and then obtain the starting position.

## Other language versions

### Java

// Solution 1
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int sum = 0;
int min = 0;
for (int i = 0; i < gas.length; i++) {
sum += (gas[i] - cost[i]);
min = Math.min(sum, min);
}

if (sum < 0) return -1;
if (min >= 0) return 0;

for (int i = gas.length - 1; i > 0; i--) {
min += (gas[i] - cost[i]);
if (min >= 0) return i;
}

return -1;
}
}
// Solution 2
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int curSum = 0;
int totalSum = 0;
int index = 0;
for (int i = 0; i < gas.length; i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0) {
index = (i + 1) % gas.length ;
curSum = 0;
}
}
if (totalSum < 0) return -1;
return index;
}
}

### Python

class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
start = 0
curSum = 0
totalSum = 0
for i in range(len(gas)):
curSum += gas[i] - cost[i]
totalSum += gas[i] - cost[i]
if curSum < 0:
curSum = 0
start = i + 1
if totalSum < 0: return -1
return start

### Go

func canCompleteCircuit(gas []int, cost []int) int {
curSum := 0
totalSum := 0
start := 0
for i := 0; i < len(gas); i++ {
curSum += gas[i] - cost[i]
totalSum += gas[i] - cost[i]
if curSum < 0 {
start = i+1
curSum = 0
}
}
if totalSum < 0 {
return -1
}
return start
}

-------------end------------

Hello, I'm Carl, senior brother of Harbin Institute of technology. I've won ACM Asian medal. I've been in two mining pits in BAT successively, and I'm a programmer with both literature and dance skills.