一、赢得比赛需要的最少训练时长
你正在参加一场比赛,给你两个 正 整数 initialEnergy 和 initialExperience 分别表示你的初始精力和初始经验。
另给你两个下标从 0 开始的整数数组 energy 和 experience,长度均为 n 。
你将会 依次 对上 n 个对手。第 i 个对手的精力和经验分别用 energy[i] 和 experience[i] 表示。当你对上对手时,需要在经验和精力上都 严格 超过对手才能击败他们,然后在可能的情况下继续对上下一个对手。
击败第 i 个对手会使你的经验 增加 experience[i],但会将你的精力 减少 energy[i] 。
在开始比赛前,你可以训练几个小时。每训练一个小时,你可以选择将增加经验增加 1 或者 将精力增加 1 。
返回击败全部 n 个对手需要训练的 最少 小时数目。
示例 1:
输入: initialEnergy = 5, initialExperience = 3, energy = [1,4,3,2], experience = [2,6,3,1]
输出: 8
解释: 在 6 小时训练后,你可以将精力提高到 11 ,并且再训练 2 个小时将经验提高到 5 。
按以下顺序与对手比赛:
- 你的精力与经验都超过第 0 个对手,所以获胜。
精力变为:11 - 1 = 10 ,经验变为:5 + 2 = 7 。
- 你的精力与经验都超过第 1 个对手,所以获胜。
精力变为:10 - 4 = 6 ,经验变为:7 + 6 = 13 。
- 你的精力与经验都超过第 2 个对手,所以获胜。
精力变为:6 - 3 = 3 ,经验变为:13 + 3 = 16 。
- 你的精力与经验都超过第 3 个对手,所以获胜。
精力变为:3 - 2 = 1 ,经验变为:16 + 1 = 17 。
在比赛前进行了 8 小时训练,所以返回 8 。
可以证明不存在更小的答案。
示例 2:
输入: initialEnergy = 2, initialExperience = 4, energy = [1], experience = [3]
输出: 0
解释: 你不需要额外的精力和经验就可以赢得比赛,所以返回 0 。
提示:
n == energy.length == experience.length1 <= n <= 1001 <= initialEnergy, initialExperience, energy[i], experience[i] <= 100
解析
今天周赛的题目是亚马逊出的,果然不一样。
这一道题就很亚马逊,参加过亚马逊面试笔试的人都应该很清楚他们的题目风格,就是说上一大堆。在说上一大堆的时候,就很难判断它的难度了,有可能会超级大,有可能就是一个脑筋急转弯。
这道题考虑到n只有100,其实就遍历逐个试试就好。先看经验值,简化之后就是要求前n项的经验值之和要严格大于后一项。 再看能量值,简化之后就是初始能量值要大于对手总能量值。
代码
class Solution:
def minNumberOfHours(self, initialEnergy: int, initialExperience: int, energy: List[int], experience: List[int]) -> int:
if len(experience) > 1:
t_exp = [0]
t_exp.extend(experience)
for exp in range(initialExperience, 200):
t_exp[0] = exp
found = True
for i in range(1, len(t_exp)):
if sum(t_exp[:i]) <= t_exp[i]:
found = False
break
if found == False:
continue
else:
break
need_experience = t_exp[0] - initialExperience
total_energy = sum(energy)
if initialEnergy > total_energy:
need_energy = 0
else:
need_energy = total_energy - initialEnergy + 1
else:
total_energy = sum(energy)
if initialEnergy > total_energy:
need_energy = 0
else:
need_energy = total_energy - initialEnergy + 1
if initialExperience > experience[0]:
need_experience = 0
else:
need_experience = experience[0] - initialExperience + 1
print(need_experience, need_energy)
return need_experience + need_energy
二、最大回文数字
给你一个仅由数字(0 - 9)组成的字符串 num 。
请你找出能够使用 num 中数字形成的 最大回文 整数,并以字符串形式返回。该整数不含 前导零 。
注意:
- 你 无需 使用
num中的所有数字,但你必须使用 至少 一个数字。 - 数字可以重新排序。
示例 1:
输入: num = "444947137"
输出: "7449447"
解释:
从 "44494 7137" 中选用数字 "4449477",可以形成回文整数 "7449447" 。
可以证明 "7449447" 是能够形成的最大回文整数。
示例 2:
输入: num = "00009"
输出: "9"
解释:
可以证明 "9" 能够形成的最大回文整数。
注意返回的整数不应含前导零。
提示:
1 <= num.length <= 10^5num由数字(0 - 9)组成
解析
这道题目其实就是要数一数每个数字出现了几次,出现次数大于等于两次的,就可以在回文数字里使用上。最后再看看出现了奇数次的数字,哪个可以放在中间对称轴的位置即可。
这道题在周赛的时候被第56个测试case卡住了,因为是最后几个case,也不会告知输入是什么,就很麻烦。
其实是因为一个自己代码的一个小bug,没有处理好输入是仅有偶数个0的情况导致的。最开始的代码写的bug是,当入参是仅有偶数个0的时候返回了空字符串,而不是字符串0。正好第56个case就是偶数个0。
代码
from collections import defaultdict
class Solution:
def largestPalindromic(self, num: str) -> str:
if len(num) == 1:
return num
if set(num) == {"0"}: # 特殊情况直接在这儿处理掉
return '0'
counts = defaultdict(int)
for n in num:
counts[n] += 1
x = ''
for p in [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]:
p = str(p)
if counts[p] >= 2 :
cnt = counts[p] // 2 # x是回文数左半边,计数除以2向下取整
counts[p] -= cnt * 2 # 对称之后就是cnt的两倍,把counts减下来
x += p * cnt
if x and x[0] == '0':
x = ''
y = x[::-1]
for p in [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]:
p = str(p)
if counts[p] == 1:
x += p
break
res = x + y
return res
三、感染二叉树需要的总时间
给你一棵二叉树的根节点 root ,二叉树中节点的值 互不相同 。另给你一个整数 start 。在第 0 分钟,感染 将会从值为 start 的节点开始爆发。
每分钟,如果节点满足以下全部条件,就会被感染:
- 节点此前还没有感染。
- 节点与一个已感染节点相邻。
返回感染整棵树需要的分钟数 。
示例 1:
输入: root = [1,5,3,null,4,10,6,9,2], start = 3
输出: 4
解释: 节点按以下过程被感染:
- 第 0 分钟:节点 3
- 第 1 分钟:节点 1、10、6
- 第 2 分钟:节点5
- 第 3 分钟:节点 4
- 第 4 分钟:节点 9 和 2
感染整棵树需要 4 分钟,所以返回 4 。
示例 2:
输入: root = [1], start = 1
输出: 0
解释: 第 0 分钟,树中唯一一个节点处于感染状态,返回 0 。
提示:
- 树中节点的数目在范围
[1, 10^5]内 1 <= Node.val <= 10^5- 每个节点的值 互不相同
- 树中必定存在值为
start的节点
解析
一开始思路有误,以为只需要知道二叉树有多少层就可以。后来发现,只需要按照题目的要求进行模拟即可。
题目的难点在于,如何做到根据节点的值达到倒排索引的效果,找到节点,以及它的父节点。 通过常规的前序遍历或者任意一种遍历方式,就可以做好这个倒排索引,为后续处理带来方便。
在已知一个起点的情况下,就可以知道下一步会被感染的点有哪些,也就知道了下一步的起点,对于已经感染过的点,就直接从总体里面扣除掉即可,可以使用集合或者数组都ok。
代码
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorder(self, root: TreeNode):
if root == None:
return None
self.all.add(root.val)
self.node_dict[root.val] = root
if root.left:
self.parent[root.left.val] = root.val
if root.right:
self.parent[root.right.val] = root.val
self.preorder(root.left)
self.preorder(root.right)
def amountOfTime(self, root: Optional[TreeNode], start: int) -> int:
self.parent = dict() # 根据节点值找到父节点的值
self.parent[root.val] = '#'
self.all = set()
self.node_dict = dict() # 根据节点值找到对应节点
self.preorder(root)
print(self.all)
print(self.parent)
starter = [start]
steps = 0
while starter:
next_starter = []
for s in starter:
self.all.remove(s) # 已经感染过的节点直接remove掉
n = self.node_dict[s]
p = self.parent[s]
if p != '#':
if p in self.all:
next_starter.append(p)
if n.left:
if n.left.val in self.all:
next_starter.append(n.left.val)
if n.right:
if n.right.val in self.all:
next_starter.append(n.right.val)
if next_starter:
steps += 1
starter = next_starter
print(steps)
return steps