代码随想录贪心算法——加油站

题目

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

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i]升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明: 如果题目有解,该答案即为唯一答案。 输入数组均为非空数组,且长度相同。 输入数组中的元素均为非负数。
示例 1:
输入:gas = [1,2,3,4,5] cost = [3,4,5,1,2]
输出: 3
解释:从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油 开往 4 号加油站,此时油箱有 4- 1 + 5 = 8 升汽油 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油 开往 3 号加油站,你需要消耗 5升汽油,正好足够你返回到 3 号加油站。 因此,3 可为起始索引。
示例 2:
输入:gas = [2,3,4] cost = [3,4,3]
输出: -1
解释: 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。我们从 2 号加油站出发,可以获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油。开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油。开往 1号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油。你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3升汽油。因此,无论怎样,你都不可能绕环路行驶一周。

思路

  1. 暴力方法:

遍历每个加油站为起点的情况,模拟一圈;如果跑了一圈,中途没有断油,而且最后油量大于等于0,说明这个七点是ok的

for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历

class Solution {
	public int canCompleteCircuit(int[] gas, int[] cost){
		for(int i = 0; i < cost.length; i++){
			int res = gas[i] - cost[i];// 记录剩余油量
			int index = ( i + 1 ) % cost.lenghth;
			while(res > 0 && index != i){// 模拟以i为起点行驶一圈
				res += gas[index] - cost[index];
				index = (index + 1) % cost.length;
			}
			// 如果以i为起点跑一圈,剩余油量>=0,返回该起始位置
			if(res >= 0 && index == i){
				return i;
			}
		}
		return -1;
	}
}
  1. 贪心策略:

贪心一(伪贪心,因为是直接从全局最优考虑问题):

直接从局部进行贪心:

  • 如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的
  • rest[i] = gas[i]-cost[i]为一天剩下的油,i0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点
  • 如果累加的最小值负数,汽车就要从非0节点出发,从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点
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;
    }
}

贪心二(全局最优是通过局部最优推导出来的):

如果总油量减去总消耗大于等于0,那么一定可以跑完一圈,说明各个站点的加油站剩余量res[i]相加一定是大于等于0

每个加油站的剩余量res[i] = gas[i] - cost[i]

i0开始累加res[i],和标记为curSum,一旦curSum < 0,说明[0,i]区间内都不能作为起始位置,起始位置从i+1算起,curSum0开始计算
在这里插入图片描述
上图相当于下标0res为负数,所以从下标1开始计算,然后下标为1的剩余res仍然是负数,继续后移

局部最优 :当前累加res[i]的和curSum一旦小于0,起始位置至少是要从i+1开始,

全局最优 :找到可以跑完一圈的位置

class Solution {
	public int canCompleteCircuit(int[] gas,int[] cost){
		int curSum = 0;//记录局部剩余油量,因为有负数的话需要重新i+1的位置开始统计,并且清空累加和
		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;//从i+1的位置继续累计
				curSum = 0;
			}
		}
		if(totalSum < 0){
			return -1;
		}
		return index;
	}
}

本题给出了两种贪心算法,对于第一种贪心方法,其实认为就是一种直接从全局选取最优的模拟操作;

对于第二种贪心方法,才真正体现出贪心的精髓,用局部最优可以推出全局最优,进而求得起始位置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HDU-五七小卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值