leetcode算法总结

一.leetcode 5254. 卖木头块(动态规划)
给你两个整数 m 和 n ,分别表示一块矩形木块的高和宽。同时给你一个二维整数数组 prices ,其中 prices[i] = [hi, wi, pricei] 表示你可以以 pricei 元的价格卖一块高为 hi 宽为 wi 的矩形木块。
每一次操作中,你必须按下述方式之一执行切割操作,以得到两块更小的矩形木块:
沿垂直方向按高度 完全 切割木块,或
沿水平方向按宽度 完全 切割木块
在将一块木块切成若干小木块后,你可以根据 prices 卖木块。你可以卖多块同样尺寸的木块。你不需要将所有小木块都卖出去。你 不能 旋转切好后木块的高和宽。

请你返回切割一块大小为 m x n 的木块后,能得到的 最多 钱数。

注意你可以切割木块任意次。

题目分析:这道题目的一个十分好用的优化解法是动态规划。(注意,动态规划可以理解为多维的递归)


以下是正解:

class Solution:
    def sellingWood(self, m: int, n: int, prices: List[List[int]]) -> int:
        pr = {(h,w):p for [h,w,p] in prices}
        f = [[0]*(n+1) for i in range(m+1)]
        for i in range(1,m+1):
            for j in range(1,n+1):
                f[i][j]=max(pr.get((i,j),0),
                max([f[i][k]+f[i][j-k] for k in range(1,j)],default=0),
                max([f[k1][j]+f[i-k1][j] for k1 in range(1,i)],default=0))
        return f[m][n]
(记得自己写一遍试试呀)
二、2304. 网格中的最小路径代价(递归+@lru_cache(None))
给你一个下标从 0 开始的整数矩阵 grid ,矩阵大小为 m x n ,由从 0 到 m * n - 1 的不同整数组成。你可以在此矩阵中,从一个单元格移动到 下一行 的任何其他单元格。如果你位于单元格 (x, y) ,且满足 x < m - 1 ,你可以移动到 (x + 1, 0), (x + 1, 1), ..., (x + 1, n - 1) 中的任何一个单元格。注意: 在最后一行中的单元格不能触发移动。

每次可能的移动都需要付出对应的代价,代价用一个下标从 0 开始的二维数组 moveCost 表示,该数组大小为 (m * n) x n ,其中 moveCost[i][j] 是从值为 i 的单元格移动到下一行第 j 列单元格的代价。从 grid 最后一行的单元格移动的代价可以忽略。

grid 一条路径的代价是:所有路径经过的单元格的 值之和 加上 所有移动的 代价之和 。从 第一行 任意单元格出发,返回到达 最后一行 任意单元格的最小路径代价。

题目分析:运用递归可以减少程序复杂度;
运用@lru_cache(None)可以减少存储空间和运行时间。

正解:
class Solution:
    def minPathCost(self, grid: List[List[int]], moveCost: List[List[int]]) -> int:
        m = len(grid)
        n=len(grid[0])
        # 很多问题会造成出错,不如先改成递归的形式,简洁的形式有助于提高正确率
        @lru_cache(None)
        def quasi_dfs(row, col):#递归需要两点,一个是递归链条(每次row+1),一个是终止条件(row==m-1)。
            if row==m-1:
                return grid[row][col]  
            else:
                l2=[0]*n
                for i in range(n):
                    l2[i]=moveCost[grid[row][col]][i]+quasi_dfs(row+1,i)
                return grid[row][col]+min(l2)
        l1=[]
        for i in range(n):
            l1.append(quasi_dfs(0,i))
        return min(l1)
            
(记得自己写一遍试试呀)
20220625双周赛
三、6106. 统计无向图中无法互相到达点对数(O(n)的dfs求连通分支)
给你一个整数 n ,表示一张 无向图 中有 n 个节点,编号为 0 到 n - 1 。同时给你一个二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示节点 ai 和 bi 之间有一条 无向 边。

请你返回 无法互相到达 的不同 点对数目 。

我的一个成功但是超时了的解答。(我的解答完全是自己写的,几乎没有用到任何数据结构或者是成熟的算法)

这道题的关键在于:深度优先搜索。(你自己想的算法的复杂度很有可能比dfs高)

class Solution:
    def countPairs(self, n: int, edges: List[List[int]]) -> int:
        #这又是一道超出了时间限制的题目,需要注意复杂度。也许这其中有
        l1=[]
        for i in range(n):
            l1.append({str(i)})#O(n)
        for edge in edges:
            for set1 in l1:
                if str(edge[0]) in set1:
                    s1=set1.copy()
                    break
            for set2 in l1:
                if str(edge[1]) in set2:
                    s2=set2.copy()
                    break#这两个查找,复杂度O(n)
            if s1!=s2:
                l1.remove(s1)
                l1.remove(s2)
                s3=s1|s2
                l1.append(s3)
        l2=[]
        for s in l1:
            l2.append(len(s))
        sum=0
        for l in l2:
            sum+=l*(n-l)
        return int(sum/2)  # 所以最后的复杂度为O(mn),所以你自己发展出来的算法往往比已经成熟的算法好的可能性低。所以学习,去掌握那些已经发展好的算法。
# 别人的正确的答案 分析异同 和之前的题目一起。
# class Solution:
#     def countPairs(self, n: int, edges: List[List[int]]) -> int:
#         g = [[] for _ in range(n)]
#         for x, y in edges:
#             g[x].append(y)
#             g[y].append(x)# 复杂度为O(m)
#
#         vis, ans, tot, size = [False] * n, 0, 0, 0
#         def dfs(x: int) -> None:
#             nonlocal size
#             vis[x] = True
#             size += 1
#             for y in g[x]:
#                 if not vis[y]:
#                     dfs(y)
# 这个dfs运行之后结果如何?dfs积累起来,复杂度为O(n)
#         for i in range(n):
#             if not vis[i]:
#                 size = 0
#                 dfs(i)
#                 ans += size * tot
#                 tot += size
#         return ans
#
# 作者:endlesscheng
# 链接:https://leetcode.cn/problems/count-unreachable-pairs-of-nodes-in-an-undirected-graph/solution/by-endlesscheng-7l50/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

总之,优先使用成熟的算法可以使得复杂度降低。

(记得自己写一遍试试呀)
20220626单周赛
5229.拼接数组的最大分数(最大字数组和,通过观察,找到O(n)的算法)
给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,长度都是 n 。

你可以选择两个整数 left 和 right ,其中 0 <= left <= right < n ,接着 交换 两个子数组 nums1[left...right] 和 nums2[left...right] 。

例如,设 nums1 = [1,2,3,4,5] 和 nums2 = [11,12,13,14,15] ,整数选择 left = 1 和 right = 2,那么 nums1 会变为 [1,12,13,4,5] 而 nums2 会变为 [11,2,3,14,15] 。
你可以选择执行上述操作 一次 或不执行任何操作。

数组的 分数 取 sum(nums1) 和 sum(nums2) 中的最大值,其中 sum(arr) 是数组 arr 中所有元素之和。
返回 可能的最大分数 。
子数组 是数组中连续的一个元素序列。arr[left...right] 表示子数组包含 nums 中下标 left 和 right 之间的元素(含 下标 left 和 right 对应元素)。

提示:

n == nums1.length == nums2.length
1 <= n <= 105
1 <= nums1[i], nums2[i] <= 104
以下再贴一个我的自己解决的但是超时了的,复杂度为O(n^2)的程序:
这个程序算是遍历吧。也算是动态规划?(是的)
class Solution:
    def maximumsSplicedArray(self, nums1: List[int], nums2: List[int]) -> int:
        #用动态规划,这就又超出时间限制了,先把你能想到的都试一试 好吧,我不会
        n=len(nums1)
        s1=sum(nums1)
        s2=sum(nums2)
        a1=max(s1,s2)
        for left in range(n):
            s3=[0]*(n+1)
            s4=[0]*(n+1)
            for right in range(left,n):
                s3[right]=s3[right-1]+nums1[right]
                s4[right]=s4[right-1]+nums2[right]
                inter=s3[right]-s4[right]
                a2=s1-inter
                a3=s2+inter
                a1=max(a1,a2,a3)
        return a1
别人的解答的成功之处在于复杂度是O(n).关键还是在性质刻画。最大子数组和有复杂度为O(n)的算法。你能不能通过观察,总结出来一个复杂度大大降低的算法?
别人的通过了的解答:
class Solution:
    def maximumsSplicedArray(self, nums1: List[int], nums2: List[int]) -> int:

        def maxSubArray(nums: List[int]) -> int:
            pre = 0
            res = nums[0]

            for num in nums:
                pre = max(pre + num, num)
                res = max(res, pre)

            return res

        diff1, diff2 = [], []
        for num1, num2 in zip(nums1, nums2):
            diff1.append(num1 - num2)
            diff2.append(num2 - num1)

        res = max(
            sum(nums1) + maxSubArray(diff2),
            sum(nums2) + maxSubArray(diff1)
        )

        return res

(记得自己写一遍试试呀)

20220703单周赛第二题

6111. 螺旋矩阵 IV
给你两个整数:m 和 n ,表示矩阵的维数。

另给你一个整数链表的头节点 head 。

请你生成一个大小为 m x n 的螺旋矩阵,矩阵包含链表中的所有整数。链表中的整数从矩阵 左上角 开始、顺时针 按 螺旋 顺序填充。如果还存在剩余的空格,则用 -1 填充。

返回生成的矩阵。
别人的成功的代码:在这道题目中,我连ListNode的用法都不知道,程序写得一团乱麻。所以就先研究一下别人的算法吧
这一次,就不该抱怨为什么自己没有想出来了,而是缺少练习。

下面是正解:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def spiralMatrix(self, m: int, n: int, head: Optional[ListNode]) -> List[List[int]]:

        res = [[-1 for _ in range(n)] for _ in range(m)]

        visited = [[False for _ in range(n)] for _ in range(m)]

        dirs = ((0, 1), (1, 0), (0, -1), (-1, 0))
        row, col = 0, 0
        idx = 0

        while head:
            res[row][col] = head.val
            visited[row][col] = True

            new_row, new_col = row + dirs[idx][0], col + dirs[idx][1]
            if not (0 <= new_row < m and
                    0 <= new_col < n and
                    not visited[new_row][new_col]):
                idx = (idx + 1) % 4

            row += dirs[idx][0]
            col += dirs[idx][1]

            head = head.next

        return res

20220705每日一题中用到的好的point:

bisect非常好用。

还有这个:

from itertools import pairwise
a=pairwise('123')
for i in a:
...     print(i)
...     
('1', '2')
('2', '3')

夸一夸下面这段代码:它的优势在于:1.首先for a,b in pairwise(sorted(arr))增大了生成成对数据的效率。2.其次 if (cur:=b-a)<m:这一段简化了表达。3.还有m,ans=cur,[[a,b]],这样的赋值语句比一般的赋值语句省时间。

class Solution:
    def minimumAbsDifference(self, arr: List[int]) -> List[List[int]]:
        m, ans = inf, []
        for a, b in pairwise(sorted(arr)):
            if (cur := b - a) < m:
                m, ans = cur, [[a, b]]
            elif cur == m:
                ans.append([a, b])
        return ans

作者:himymBen
链接:https://leetcode.cn/problems/minimum-absolute-difference/solution/-by-himymben-o6ub/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刺蓟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值