LeetCode 134. 加油站【中等】

80 阅读2分钟

题干

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

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

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

题解

对于一个点来说,能否到达下一个点取决于当前剩余油量和到达下一个站点所需要的油量,如果剩余油量>所需油量,那么通过该点可以到达下一个点;反之,则不行。考虑从某一个点出发前往相邻节点的情况,即当前剩余油量为0,可以在当前站加油,所以剩余油量为当前加油站的油量,即,gas[i],所需油量为cost[i],所以判断能否从i出发前往下一个点的条件为gas[i] - cost[i] >= 0。所以就有一个非常简单的想法,遍历每一个可能的起点,检查它是否可以循环完一遍,判断它可以循环完一遍的条件为,表达式gas[i] - cost[i]遍历过程中的前缀和是>=0的。在这个想法的基础上,应该想到在遍历的过程中,可以忽略掉一些一定不满足条件的点,即当我们发现某个起点不满足条件时,是否可以不只移动一格,可以跳跃到下一个可能的起点。考虑一个起点xy是距离它最近的不可达的点,那么xy中间的任意一个点都无法到达y,这个是好证明的,假设存在zxy之间,且z可以到达y,那么x就可以到达y,就和x不能到达y矛盾了。根据这个结论,我们可以写出如下算法。

func canCompleteCircuit(gas []int, cost []int) int {
	n := len(gas)
	for start := 0; start < n; {
		tmpSum, cnt, end := 0, 0, start
		for cnt < n {
			end = (start + cnt) % n
			tmpSum += gas[end] - cost[end]
			if tmpSum < 0 {
				break
			}
			cnt++
		}
		// 如果cnt = n,那么代表当前起点下已经循环走完一遍,找到一个满足条件的点
		// 反之,则将指针挪动到下一个起点
		if cnt == n {
			return start
		} else {
			start += cnt + 1
		}
	}
	return -1
}