Leetcode 134 加油站

题目描述:


在这里插入图片描述

思路

首先自然可以想到要选择一个出发点,使汽车能驶过每一个加油站的前提是,到达所有加油站的耗油量之和Σcost [i]]必须小于等于加油量之和Σgas[i]。当汽车行驶到某一处加油站i时,如果此时油量不足到下一站,但是后续的i+2、i+3....i+n、0站却可以顺利驶完,这是我们就需要考虑如何设计一个策略选择合适的出发点,即一个贪心策略。
采用暴力解法可以得到答案,只需两层for遍历每个加油站作为初始点,模拟计算行驶过程中耗油量和加油量变化,这里先给出暴力解法:
	public int canCompleteCircuit(int[] gas, int[] cost) {
    int n = gas.length;
    for (int i = 0; i < n; i++) {
        int j = i;
        int remain = gas[i];
        //当前剩余的油能否到达下一个点
        while (remain - cost[j] >= 0) {
            //减去花费的加上新的点的补给
            remain = remain - cost[j] + gas[(j + 1) % n];
            j = (j + 1) % n;
            //j 回到了 i
            if (j == i) {
                return i;
            }
        }
    }
    return -1;
}

可以看出最坏情况下,时间复杂度达到了O(n ^ 2),其中n为加油站的数量。显然这并不是题目的最优解法。


在最初考虑题目的时候,仅仅考虑到如何选择一个坐标点,能尽可能的多累加油量即使得初始阶段的gas[i]-cost[i]尽可能大,但是忽略了gas[i]-cost[i]的值可能并不是某局部累加油量最大就能保证行驶完全程,可能出现的情况就是最多走到第 i 个加油站,没有足够的油量驶向 i+1 站,但是后续的 i+2 …i+n站会补偿更多的油量让汽车足以驶完全程。显然考虑局部最大值的思路是错误的,但是贪心题目就是考虑最大最小的问题,我们需要更换贪心策略。这里参考了 > 程序员小灰的思路。


不妨把0点作为起始点start,需要使得从这个点开始的净油量gas[i]-cost[i]之和一直大于0才能行驶完全程。 问题转变成找出这样一个起始点start,这里参考小灰的图,用二维坐标图来形象描述加油站点与净油量的关系,净油量即Sum+=gas[i]-cost[i],如图为例
在这里插入图片描述
显然,上图将 0 作为起点肯定是不行的,因为sum在变化的过程中小于 0 了,不符合我们「累加和一直大于等于 0」的要求。
那如果 0 不能作为起点,谁可以作为起点呢?我们可以设想,如果把最低点抬高,即把坐标轴平移,那么是不是就存在可能Sum一直大于0的情况?由于题目情景是一个环形数组,最低点左侧的图像可以接到图像的最右侧,所以抬高坐标轴的思路是可行的。如果把这个「最低点」作为起点,就是说将这个点作为坐标轴原点,就相当于把图像「最大限度」向上平移了。
在这里插入图片描述

经过平移后图像一定全部在 x 轴以上吗?不一定,因为还有无解的情况:

如果sum(gas[…]) < sum(cost[…]),总油量小于总的消耗,那肯定是没办法环游所有站点的。

综上,我们就可以写出代码:

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int len = gas.length, maxcost = 0, min = 0x3f3f, start = 0;
        for (int i = 0;i < len; ++i){
        	maxcost += gas[i] - cost[i];
        	if (maxcost < min){
        		min = maxcost;
        		start = i;
        	}
        }
        if (maxcost >= 0){
            int s = (start + 1) % len;
        	return s;
        } else {
        	return -1;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值