1005.K次取反后最大化的数组和
1.题目
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)以这种方式修改数组后,返回数组可能的最大和。
2.实现
解题思路易想,但很难想全所有情况,问题在于:
在思考时取定目标,如本题就是让尽可能小的数去变号,涉及到有无负数,负数的个数与k相比,是否含有0;但如何进行顺序判断是优化解题步骤的重要方法;
有了思路之后要打好框架
class Solution:
def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
# 数组从小到大排序
nums = sorted(nums)
n = len(nums)
i = 0
while k > 0 and i < n:
if nums[i] < 0:
nums[i] = -nums[i]
i += 1
elif nums[i] == 0:
break
else:
if i > 0 and nums[i] >= nums[i - 1]: # 不能只看原来数组,改了之后是否会影响
nums[i - 1] = ((-1)**k)*nums[i - 1]
else:
nums[i] = ((-1)**k)*nums[i]
break
k -= 1
if i == n and k > 0: # 当有多个跳出条件时,考虑多个条件跳出时的情况
if nums[n - 1] < 0:
nums[i] = ((-1)**k)*nums[i]
if nums[n - 1] > 0:
if nums[n - 1] <= nums[n - 2]:
nums[n - 1] = ((-1)**k)*nums[n - 1]
else:
nums[n - 2] = ((-1)**k)*nums[n - 2]
return sum(nums)
3.文章讲解
巧妙思路:按绝对值大小进行降序排序,再遍历数组将负值反号,再讨论负值和k的大小从而牺牲最后一个(可能为0的数)
134. 加油站
1.题目
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
2.实现:
困难点:
超出时间限制,我居然连暴力都没过,想法没优化前,建议对比和暴力算法的复杂度,如果没有得到提升还不如用暴力
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
# 2 3 4 2 3 4 [-1, -1, 1 -1, -1, 1] 选择大于0的家有点出发,且保证子数组的和都是>0
# 3 4 3 3 4 3
# 1 2 3 4 5 [-2 -2 -2 3 3 -2 -2 -2 3 3]
# 3 4 5 1 2
n = len(gas)
tmp = 0
total_sum = 0
index = 0
for i in range(n):
diff = gas[i] - cost[i]
tmp += diff
total_sum += diff
if tmp < 0:
index = i + 1
tmp = 0
# print("j的值:", j, n + i)
if total_sum < 0: return -1
return index
3.文章讲解
亮点:
仔细想想自己的解法,发现会一直计算从每个点开始的耗油总量,且需保证每个子数组的耗油总量大于0,但我的思路是会重复计算,然而题解就分了两次判断
1)总耗油量一次计算且必须>0,通过遍历所有数组即得
2)每段耗油量>0只是为了确定从哪里出发,所以遍历数组时只需不断改变起点即可!
135. 分发糖果
1.题目
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
2.实现
思路1:新建了candies数组,但超出时间限制,此时极端情况可能O(n**2)的时间复杂度
思路2:在1的基础上,找准目标,要求最大和于是舍弃更新数组再去求和,而是记录res并返回,因为此时糖果分配是有规律可循的,根据评分的变化,具体看代码实现
class Solution:
def candy(self, ratings: List[int]) -> int:
n = len(ratings)
# 去统计res即可
res = 1
state = 1
i = 1
while i < n:
# 升的情况
if ratings[i] > ratings[i - 1]:
state += 1
res += state
i += 1
# 相等时
elif ratings[i] == ratings[i - 1]:
state = 1
res += state
i += 1
# 讨论连减
else:
cnt = 0
while i < n and ratings[i] < ratings[i - 1]:
i += 1
cnt += 1
res += int((cnt + 1)* cnt / 2)
if state == cnt:
res += 1
elif state < cnt:
res += (cnt - state + 1)
state = 1
if i == n:
return res
return res
实现时遇到的一个问题:
更改for循环i时,并不能改变循环次数,for循环的循环机制是range作为迭代器,每循环一次就把值赋值给i,所以循环体内改变i的值,对循环次数无影响。
3.文章讲解
思路:两个方向,对于右>左即rating[i] > rating[i - 1]的情况,只需满足candies[i] = candies[i - 1] + 1,从前往后遍历
但还需考虑左>右时是否糖果分配妥当,要根据nums[i+1]调整nums[i],且为了保留上一次遍历的结果需从后往前遍历,处理nums[i]也有了新技巧