当前位置: 首页 > news >正文

【贪心算法】加油站

🌈题目链接:加油站

题目描述:

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

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

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

✅暴力解法:

看到这个题目,由于数组是定长的,每一个加油站都可能作为起点,我们只需要暴力遍历每个加油站,再尝试从每个加油站出发,遍历一周,看是否能环绕一圈。如果能,我们就直接返回这个起点即可。小tips,我们可以把cost数组和gas 看成一个diff净收益数组,这样想能清楚点。

代码如下:

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {for(int i=0; i<gas.size(); i++){int totalsum = 0;for(int step=0; step<gas.size(); step++){int nextstep = (i+step) % gas.size();totalsum +=  gas[nextstep] - cost[nextstep];if(totalsum <0){break;}}if(totalsum >= 0){return i;}}return -1;}

由于n的范围是10^5,暴力时间复杂度为O(N^2),必定超时。

我们来优化一下这个线性的暴力枚举

✅贪心法

我们来思考一下,我们需要挨个加油站一个个遍历吗,能不能舍去一些没必要的加油站。我们把cost和gas数组看成一个净利益数组,如图:

假设从a加油站出发,最多能走到f,走到f就发现再走不了了。此时我们有必要再从b,c,e加油站挨个尝试吗?没必要了!why?

我们可以简单证明一下:

由于a最多能走到f,所以a+b+c+e+f<0,由于a>0(因为a能走到b),所以b+c+e一定是<0

所以从b.c.e出发,也一定不能到f。因为(b,c,e)加一个>0的数(a)都不能走到f,那么(b,c,e)肯定不能到达了。

既然知道了这个道理,我们就可以从一个加油站尝试出发比如start,看他最远能走到哪里就不能走了比如end。那么下一次我们加油站的起点一定是end+1位置。这期间[start,end]的所有点都不能作为起点了!

我们在暴力枚举的基础上简单优化即可:

即更新加油的位置稍做调整:

  int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {for (int i = 0; i < gas.size(); i++) {int sum = 0, step = 0;for (; step < gas.size(); step++) {int currenti = ((step + i) % gas.size()); // 求出下一步sum += gas[currenti] - cost[currenti];if (sum < 0) {break;}}if (sum >= 0)return i;i = i + step; // 这之间i<->i+step之间的所有点都不能作为起点}return -1;}

最差情况,我们线性枚举两次,也一定更新出加油站的位置。所以时间复杂度为O(N)

✅换种更优雅的代码。也是利用贪心

观察一:我们用一个变量记所有加油站的和totalsum。那么我们就不难得出一个结论,如果该和<0,那么无论从哪个起点出发,都不能环绕一圈。

观察二:我们只考虑diff净利益数组,那么如果当前加油站的利益currsum小于0,那么当前加油站一定不能作为起点。

观察三:如果从当前加油站出发,能走到终点,并且totalsum还是>0的,那么该加油站就是能环绕一圈的起点(答案唯一),因为该加油站前面的total已经计算了,该加油站再把剩下几站走完整个total还能>0。

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int totalsum = 0, targeti = 0, currsum = 0;for(int i=0; i<gas.size(); i++){currsum += gas[i] - cost[i];totalsum += gas[i] - cost[i];if(currsum < 0){// 不可达下一步currsum = 0;targeti = i+1; // 目标起点一定不在i+1之前}}return totalsum >=0 ? targeti : -1;}

http://www.lryc.cn/news/619402.html

相关文章:

  • TensorFlow深度学习实战(31)——强化学习仿真库Gymnasium
  • 【IntelliJ IDEA】如何在pom.xml中去除maven中未使用的依赖
  • 七大排序算法全解析:从入门到精通
  • 各种排序算法(一)
  • Java开发环境搭建(WIN+IDEA+Maven)
  • STM32的UART奇偶校验注意
  • C# xml UI格式化字符串
  • IDEA创建一个VUE项目
  • 基于人工智能和物联网融合跌倒监控系统(LW+源码+讲解+部署)
  • CW32L011电机开发板控制教程
  • 分布式与微服务宝典
  • js:13KB或者xxxkb、xxxMB\xxxtb\xxb等多种情况怎么获取后面的单位
  • FluxSelectMultiple 技术设计文档
  • 飞算JavaAI的中间件风暴:Redis + Kafka 全链路实战
  • docker network 与host的区别
  • 数据科学与爬虫技术学习笔记
  • 玩转Docker | 使用Docker部署WordPress网站服务
  • 【车联网kafka】Kafka核心架构与实战经验(第四篇)
  • 猿大师中间件:Chrome网页内嵌PhotoShop微信桌面应用程序
  • Jetson NX Python环境搭建:使用APT轻松安装NumPy, scikit-learn, OpenCV
  • java学习 leetcode 二分查找 图论
  • 图论理论部分
  • 【C++ STL】list详解和模拟
  • Day52--图论--101. 孤岛的总面积(卡码网),102. 沉没孤岛(卡码网),103. 水流问题(卡码网),104. 建造最大岛屿(卡码网)
  • day50 图论基础 卡码网98. 所有可达路径
  • 15-docker的企业级私有仓库之docker-harbor
  • 若依plus SpringCloud [DUBBO] 多模块异常抛出 异常里面包了一层异常
  • docker load镜像后 名字和标签异常解决
  • 【Docker项目实战】使用Docker部署todo任务管理器
  • 飞算JavaAI云原生实践:基于Docker与K8s的自动化部署架构解析