日渐头秃的代码日记--第307场周赛通过3题

133 阅读4分钟

一、赢得比赛需要的最少训练时长

你正在参加一场比赛,给你两个  整数 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.length
  • 1 <= n <= 100
  • 1 <= 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^5
  • num 由数字(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

image.png