python强化20日(蓝桥日)
第三天 Leetcode 贪心算法,蓝桥样题,回溯
贪心
45 跳跃游戏2(贪心算法)
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
#45 跳跃游戏2(贪心算法)
class Solution:
def jump(self, nums):
step=0
end=0
max_bound=0
for i in range(len(nums)-1): #定义nums-1目的是为了如果输入只有一个数,则会跳过直接认为是0
max_bound=max(max_bound,nums[i]+i) #最大边界在随着遍历实时寻找最大边界
if(i==end): #当i触碰到最大边界说明完成一次贪心
step+=1
end=max_bound #最大边界更新
return step
main = Solution()
print(main.jump([0]))
- 重点,流程:
设定变量:步数,当前最大边界值,更新最大边界值
遍历一遍,注意遍历到n-1,保证如果刚进遍历时边界值为1直接跳出 - 总结:
贪心算法可以归结为一类实时找边界值的问题
134 加油站(贪心算法)
在一条环路上有 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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
- 官方解答
定一个出发点,看看是否可以往下走,一直到油箱不足则无法往下走,则在这一点之前的任何一点作为起点都无法继续走,那么起点要更替到下一个
#如果这一点到下一点能走,那么下一点起始的油大于0,则如果这一点能走到头,起点不可能为下一点
#134 加油站(贪心算法)(解答)
#官方解答
class Solution1:
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int
"""
n = len(gas)
total_tank, curr_tank = 0, 0
starting_station = 0
for i in range(n):
total_tank += gas[i] - cost[i]
curr_tank += gas[i] - cost[i]
# If one couldn't get here,
if curr_tank < 0: #定一个出发点,看看是否可以往下走,一直到油箱不足则无法往下走,则在这一点之前的任何一点作为起点都无法继续走,那么起点要更替到下一个
#如果这一点到下一点能走,那么下一点起始的油大于0,则如果这一点能走到头,起点不可能为下一点
# Pick up the next station as the starting one.
starting_station = i + 1
# Start with an empty tank.
curr_tank = 0
return starting_station if total_tank >= 0 else -1
- 解答2
根据规则的做法,因为答案是唯一的,所以汽车进行一次循环后油箱剩余油量最小,所以通过从前向后找到最小的油箱剩余量即可找到出发点位置
class Solution2:
def canCompleteCircuit(self, gas, cost):
rest = 0
oil_minvalue = 0
record_point = -1
for i in range(len(gas)):
rest += gas[i] - cost[i]
if rest < oil_minvalue:
oil_minvalue = rest
record_point = i
return -1 if rest < 0 else record_point + 1
- 自己解答,更贴近贪心
更加接近贪心算法的思路
如果要循环一圈,汽车需要屯够足够的油,则汽车先把囤油路线走掉则最有可能到达
关键点:从后往前遍历,如果有出现tun比原先大,则这一点出发肯定能先走屯油路线,否则一定走的存在开头消耗的路线
自己写
class Solution3:
def canCompleteCircuit(self, gas, cost):
max_tun = gas[len(gas)-1]-cost[len(gas)-1]
tun = 0
start_point = len(gas)-1
for i in range(len(gas)-1,-1,-1):
tun += gas[i]-cost[i]
if tun > max_tun:
max_tun = tun
start_point = i
return start_point if tun>=0 else -1
main = Solution3()
print(main.canCompleteCircuit([3,1,1],[1,2,2]))
135 分发糖果
- 左右两次遍历挑选最大值
class Solution:
def candy(self, ratings):
result_left = [1]*len(ratings)
result_right = [1]*len(ratings)
for i in range(len(ratings)-1):
if ratings[i] < ratings[i+1]:
result_right[i+1] = result_right[i]+1#保证右边大于左边时右边多1
for i in range(len(ratings)-1,0,-1): #保证左边大于右边时左边多1
if ratings[i] < ratings[i-1]:
result_left[i-1] = result_left[i]+1
#此处取两次结果每一次的最大值,保证两个条件都满足的同时,每一个所用糖果最小
result = [max(result_right[i],result_left[i]) for i in range(len(ratings))]
return sum(result)
- 不取最大值版
class Solution2:
def candy(self, ratings):
result = [1]*len(ratings)
for i in range(len(ratings)-1):
if ratings[i] < ratings[i+1]:
result[i+1] = result[i]+1#保证右边大于左边时右边多1
for i in range(len(ratings)-1,0,-1): #保证左边大于右边时左边多1
if ratings[i] < ratings[i-1] and result[i] >= result[i-1]: #当左边大于右边且结果内容不满足左大于右时
result[i-1] = result[i]+1
return sum(result)
- 去掉去最大的步骤,第二次外直接加一个判断(网络参考版Solution2)
class Solution3:
def candy(self, ratings):
length = len(ratings)
ans = [1] * length
for i in range(1, length):
if ratings[i] > ratings[i - 1]:
ans[i] = ans[i - 1] + 1
for i in range(length - 2, -1, -1):
if ratings[i] > ratings[i + 1] and ans[i] <= ans[i + 1]:
ans[i] = ans[i + 1] + 1
return sum(ans)
main = Solution2()
print(main.candy([1,2,2]))
蓝桥杯样题
样题1:矩形切割(结果填空题)
【问题描述】
小明有一些矩形的材料,他要从这些矩形材料中切割出一些正方形。
当他面对一块矩形材料时,他总是从中间切割一刀,切出一块最大的正方形,剩下一块矩形,然后再切割剩下的矩形材料,直到全部切为正方形为止。
例如,对于一块两边分别为5和3的材料(记为5×3),小明会依次切出3×3、2×2、1×1、1×1共4个正方形。
现在小明有一块矩形的材料,两边长分别是2019和324。请问小明最终会切出多少个正方形?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
说明:以上是问题描述的部分,选手做题时可以直接手算答案,即按照题目意思一步一步切割,最后得到切出的矩形个数,手算可能花费一些时间。如果选手在手算时使用除法等方式加快速度,时间可能少一些。如果选手编写程序来计算,可以减少手算中出现的失误。
本题答案为:21
填空切正方形 程序(自己)
使用简单的递归
#蓝桥杯样题
#切出多少,不是切多少次
def main(num1,num2,i):
if num1 == num2:
print(i)
return
num1_n = max(num1,num2)
num2_n = min(num1,num2)
num1_n = num1_n - num2_n
main(num1_n,num2_n,i+1)
a = input().split(' ')
chang = int(a[0])
kuan = int(a[1])
main(chang,kuan,1)
【问题描述】
小明对数位中含有2、0、1、9的数字很感兴趣(不包括前导0),在1到40中这样的数包括1、2、9、10至32、39和40,共28个,他们的和是574。
请问,在 1 到 n 中,所有这样的数的和是多少?
【输入格式】
输入一行包含一个整数 n。
【输出格式】
输出一行,包含一个整数,表示满足条件的数的和。
【样例输入】
40
【样例输出】
574
【评测用例规模与约定】
对于20%的评测用例,1≤n≤10。
对于50%的评测用例,1≤n≤100。
对于80%的评测用例,1≤n≤1000。
对于所有评测用例,1≤n≤10000。
说明:本题是一道编程题,选手需要编写一个程序来解决问题。以下给出一个参考程序,选手所编写的其他程序只要能给出正确的结果即可得分。
官方答案
n = int(input())
ans = 0
for i in range(1,n+1):
t = i
ok = False
while t>0:
g = t % 10
if g==2 or g==0 or g==1 or g==9:
ok = True
t = t // 10
if ok:
ans += i
print(ans)
程序(自己)
想到了把数字转化成集合形式,然后取个交集看看交集存不存在
#蓝桥杯样题
#1到n含有2、0、1、9的数字
def main(num):
result = 0
for i in range(1,num+1):
if len(set(list(str(i))) & set(['2','0','1','9']))!=0:
result+=i
print(result)
num = int(input())
main(num)