Manjusaka

Manjusaka

Leetcode 每週競賽 174 題解

最近因為生病好久沒刷題,今早開始打了一場 Leetcode 的周賽,來寫個題解,今早狀態還行,,BTW 以後每週都會打周賽,爭取寫題解

Leetcode 1341. 矩陣中 K 個最弱的行#

描述:

給定一個 m * n 的矩陣 mat,矩陣中有 1(代表士兵)和 0(代表平民),返回矩陣中 k 個最弱的行的索引,按從弱到強的順序排列。
行 i 比行 j 弱,如果行 i 中的士兵數量少於行 j 中的士兵數量,或者它們的士兵數量相同但 i 小於 j。士兵總是站在行的前沿,也就是說,總是 1 可能先出現然後是 0。

範例 1:

Input: mat = 
[[1,1,0,0,0],
 [1,1,1,1,0],
 [1,0,0,0,0],
 [1,1,0,0,0],
 [1,1,1,1,1]], 
k = 3
Output: [2,0,3]
解釋: 
每行的士兵數量為: 
row 0 -> 2 
row 1 -> 4 
row 2 -> 1 
row 3 -> 2 
row 4 -> 5 
按從弱到強的順序排列的行為 [2,0,3,1,4]

題面很簡單,其實這道題就是二進制的處理,Python 裡面就暴力出奇跡了

from typing import List


class Solution:
    def kWeakestRows(self, mat: List[List[int]], k: int) -> List[int]:
        if not mat:
            return []
        number = []
        for i in range(len(mat)):
            number.append((int("".join([str(x) for x in mat[i]]), 2), i))
        number.sort()
        return [x for _, x in number[0:k]]

1342. 將數組大小減半#

描述:

給定一個數組 arr。你可以選擇一組整數並移除數組中這些整數的所有出現。
返回最小的集合大小,以便至少移除數組中的一半整數。

Input: arr = [3,3,3,3,5,5,5,2,2,7]
Output: 2
解釋: 選擇 {3,7} 將使新數組 [5,5,5,2,2] 的大小為 5(即等於舊數組大小的一半)。
可能的大小為 2 的集合有 {3,5},{3,2},{5,2}。
選擇集合 {2,7} 是不可能的,因為它將使新數組 [3,3,3,3,5,5,5] 的大小大於舊數組大小的一半。

這個題題面也很簡單,給定一個數組,選擇一組數字移除,被移除後的數組數量小於等於之前的一半,求最少選擇多少數字能達到要求

哈希表,O (N) 的做法

from typing import List


class Solution:
    def minSetSize(self, arr: List[int]) -> int:
        if not arr:
            return 0
        counter = {}
        for i in arr:
            counter[i] = counter.setdefault(i, 0) + 1
        counter = {k: v for k, v in sorted(counter.items(), key=lambda item: item[1], reverse=True)}
        total_count = 0
        result_count = 0
        for i, count in counter.items():
            total_count += count
            result_count += 1
            if total_count >= len(arr) / 2:
                break
        return result_count

1343. 分割二叉樹的最大乘積#

描述:

給定一個二叉樹的根節點。通過移除 1 條邊將二叉樹分割成兩個子樹,使得子樹的和的乘積最大化。
由於答案可能太大,請返回它對 10^9 + 7 取模。

範例 1:

image

Input: root = [1,2,3,4,5,6]
Output: 110
解釋: 移除紅色邊並得到兩棵二叉樹,和分別為 11 和 10。它們的乘積為 110 (11*10)

這個題的題面也很簡單,給定一個帶值的二叉樹,移除某個二叉樹的邊,使之分割成為兩個新的二叉樹,求兩個二叉樹和的乘積最大

最開始很多人會被這道題唬到,但是實際上這道題就是一個二叉樹的遍歷,無論前中後序遍歷,先遍歷一次二叉樹,求出二叉樹節點值的總和,以及每個節點的左子樹的和 left_sum 以及右子樹的總和 right_sum

然後再次遍歷,result=max((total_sum-left_sum)*left_sum),(total_sum-right_sum)*right_sum),result) 暴力求解即可

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def maxProduct(self, root: TreeNode) -> int:
        total_sum = self.sum_node(root)
        result = 0
        stack = []
        node = root
        while node or stack:
            while node:
                stack.append(node)
                result = max(result, ((total_sum - node.left_sum) * node.left_sum))
                result = max(result, ((total_sum - node.right_sum) * node.right_sum))
                node = node.left
            node = stack.pop()
            node = node.right
            if node:
                result = max(result, ((total_sum - node.right_sum) * node.right_sum))
                result = max(result, ((total_sum - node.left_sum) * node.left_sum))
        return result % (10 ** 9 + 7)

    def sum_node(self, root: TreeNode) -> int:
        if not root:
            return 0
        left_sum = self.sum_node(root.left)
        right_sum = self.sum_node(root.right)
        root.left_sum = left_sum
        root.right_sum = right_sum
        return left_sum + right_sum + root.val

BTW 這段代碼的 type hint 使用其實有點問題,我後面比賽完了改了一版

from typing import Optional, Tuple, List


class TreeNode:
    val: int
    left: Optional["TreeNode"]
    right: Optional["TreeNode"]

    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class TreeNodeWithSum:
    val: int
    left: Optional["TreeNodeWithSum"]
    right: Optional["TreeNodeWithSum"]
    left_sum: int
    right_sum: int

    def __init__(
        self,
        x: int,
        left: Optional["TreeNodeWithSum"],
        right: Optional["TreeNodeWithSum"],
        left_sum: int,
        right_sum: int,
    ):
        self.val = x
        self.left = left
        self.right = right
        self.left_sum = left_sum
        self.right_sum = right_sum


class Solution:
    def maxProduct(self, root: TreeNode) -> int:
        total_sum,new_root = self.sum_node(root)
        result = 0
        stack:List[TreeNodeWithSum] = []
        node = new_root
        while node or stack:
            while node:
                stack.append(node)
                result = max(result, ((total_sum - node.left_sum) * node.left_sum))
                result = max(result, ((total_sum - node.right_sum) * node.right_sum))
                node = node.left
            node = stack.pop()
            node = node.right
            if node:
                result = max(result, ((total_sum - node.right_sum) * node.right_sum))
                result = max(result, ((total_sum - node.left_sum) * node.left_sum))
        return result % (10 ** 9 + 7)

    def sum_node(
        self, root: Optional[TreeNode]
    ) -> Tuple[int, Optional[TreeNodeWithSum]]:
        if not root:
            return 0, None
        left_sum, new_left_node = self.sum_node(root.left)
        right_sum, new_right_node = self.sum_node(root.right)
        return (
            left_sum + right_sum + root.val,
            TreeNodeWithSum(
                root.val, new_left_node, new_right_node, left_sum, right_sum
            ),
        )

BTW,這道題因為數據太大,需要對 10^9+7 取模,我智障的忘了取模,WA 了兩次,罰時罰哭。。。我真的太菜了。。

1344. 跳躍遊戲 V#

描述:

給定一個整數數組 arr 和一個整數 d。在一步中,你可以從索引 i 跳到索引:
i + x 其中:i + x < arr.length 且 0 < x <= d。
i - x 其中:i - x >= 0 且 0 < x <= d。
此外,你只能從索引 i 跳到索引 j,如果 arr [i] > arr [j] 且 arr [i] > arr [k] 對於所有索引 k 在 i 和 j 之間(更正式地說 min (i, j) < k < max (i, j))。
你可以選擇數組中的任何索引開始跳躍。返回你可以訪問的最大索引數量。
注意,你不能在任何時候跳出數組。

image

Input: arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2
Output: 4
解釋: 你可以從索引 10 開始。你可以跳 10 --> 8 --> 6 --> 7,如圖所示。
注意,如果你從索引 6 開始,你只能跳到索引 7。你不能跳到索引 5,因為 13 > 9。你不能跳到索引 4,因為索引 5 在索引 4 和 6 之間且 13 > 9。
同樣,你不能從索引 3 跳到索引 2 或索引 1。

這題的題面是這樣,一個數組,裡面有若干值,你可以從任意一個位置開始跳躍,一次只能跳一個,跳的時候需要滿足規則,假定你從數組 i 位置起跳,每次可跳的範圍是 x,那麼你需要滿足

  1. i+x < arr.length 和 0<x<=d

  2. i-x >=0 和 0<x<=d

同時假設你從 i 跳往 j,那麼你需要保證 arr [i]>arr [j] 且 i 到 j 中的每個元素都滿足 arr [j]<x<arr [i],求最多能跳多少個元素

最開始覺得這題是一個雙頭 DP 的題,嫌寫起來惡心就懶得寫,,但是後面比賽完了發現其實這題其實單 DP 就能解決的,因為我們只能從高往低跳,因此我們可以先將元素排序後依次遍歷,可以得出公式為 dp[i]=max(dp[i]+dp[j]+1) 其中 j 是從 i 起可以到達的索引值,DP 部分的複雜度為 O (DN) 但是因為需要提前排序,因此整體的時間複雜度為 O (logN+DN)

from typing import List


class Solution:
    def maxJumps(self, arr: List[int], d: int) -> int:
        length = len(arr)
        dp = [1] * length
        for a, i in sorted([a, i] for i, a in enumerate(arr)):
            for di in [-1, 1]:
                for j in range(i + di, i + d * di + di, di):
                    if not (0 <= j < length and arr[j] < arr[i]):
                        break
                    dp[i] = max(dp[i], dp[j] + 1)

        return max(dp)

總結#

很久沒刷題了,手還是有點生,在前面幾個簽到題上花了時間,,而且犯了低級錯誤,,所以以後一定要堅持刷題了。。BTW 這次的周賽題感覺都很簡單,感覺像是被洩題後找的 Backup,好了就先這樣吧,我繼續臥床養病了。。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。