本篇图文是LSGO软件技术团队组织的 第二期基础算法(Leetcode)刻意练习训练营 的打卡任务。本期训练营采用分类别练习的模式,即选择了五个知识点(数组、链表、字符串、树、贪心算法),每个知识点选择了 三个简单、两个中等、一个困难 等级的题目,共计三十道题,利用三十天的时间完成这组刻意练习。
本次任务的知识点:贪心算法
贪心算法(greedy algorithm),又称贪婪算法,是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。
贪心算法在有最优子结构的问题中尤为有效。最优子结构的意思是局部最优解能决定全局最优解。简单地说,问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。
贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。
- 题号:134
- 难度:中等
- https://leetcode-cn.com/problems/gas-station/
在一条环路上有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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
第一种:模拟仿真
首先:只有第i
站的gas[i] >= cost[i]
(加入汽油数 >= 消耗汽油数),才能作为起始加油站。此时剩余油数为Remaining = gas[i] - cost[i]
。
其次:第i+1
站的剩余油数为Remaining += gas[i+1] - cost[i+1]
只有Remaining > 0
才能前往下一站,否则不能前往下一站。
以此类推,若能跑完一周,则找到起始的加油站。
public class Solution
{
public int CanCompleteCircuit(int[] gas, int[] cost)
{
for (int i = 0; i < gas.Length; i++)
{
if (gas[i] >= cost[i])
{
if (IsCan(gas,cost,i))
{
return i;
}
}
}
return -1;
}
private bool IsCan(int[] gas, int[] cost,int index)
{
int Remaining = gas[index] - cost[index];
for (int i = 1; i < gas.Length; i++)
{
int j = (index + i) % gas.Length;
Remaining += gas[j] - cost[j];
if (Remaining < 0)
{
return false;
}
}
return true;
}
}
第二种:连续求和法
易知当总加入汽油数大于等于总消耗油汽油数时,无论如何都有解,关键就在于找出起点。
又因为有解时答案唯一,故可将问题转化为求连续和法,若连续和小于等于0,那么此段中肯定不含可以作起点的油站,将下一个油站记为起点继续计算。
public class Solution
{
public int CanCompleteCircuit(int[] gas, int[] cost)
{
int total = 0;
int index = 0;
int keeping = 0;
for (int i = 0; i < gas.Length; i++)
{
int Remaining = gas[i] - cost[i];
total += Remaining;
if (keeping > 0)
{
keeping += Remaining;
}
else
{
keeping = Remaining;
index = i;
}
}
return total < 0 ? -1 : index;
}
}
python 语言
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
total, index, keeping = 0, 0, 0
for i in range(len(gas)):
remaining = gas[i] - cost[i]
total += remaining
if keeping > 0:
keeping += remaining
else:
keeping = remaining
index = i
return -1 if total < 0 else index
往期活动
LSGO软件技术团队会定期开展提升编程技能的刻意练习活动,希望大家能够参与进来一起刻意练习,一起学习进步!
我是 终身学习者“老马”,一个长期践行“结伴式学习”理念的 中年大叔。
我崇尚分享,渴望成长,于2010年创立了“LSGO软件技术团队”,并加入了国内著名的开源组织“Datawhale”,也是“Dre@mtech”、“智能机器人研究中心”和“大数据与哲学社会科学实验室”的一员。
愿我们一起学习,一起进步,相互陪伴,共同成长。
后台回复「搜搜搜」,随机获取电子资源!
欢迎关注,请扫描二维码: