TLP-Task08学习笔记


本篇为Datawhale组队学习计划第21期LeetCode精选题目组Task08学习笔记。
初学,时间有点仓促,很多解法没有详细分析,未来可能会修改,见谅。
Datawhale学习文档:
https://github.com/datawhalechina/team-learning-program/tree/master/LeetCodeTencent
(昨天忘记改标题了)

062 不同路径

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-paths

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?

思路

参考https://leetcode-cn.com/problems/unique-paths/solution/dong-tai-gui-hua-by-powcai-2/
看作一道数学题,机器人一共要向右走m-1步和向下走n-1步,并且两种走法互斥。要在m+n-2次选择中,确定m-1个向右的时机,路径 C m + n − 2 m − 1 C_{m+n-2}^{m-1} Cm+n2m1(或者n-1次向下,结果相同)。

主要考察还是动态规划
转移方程 dp[i][j] = dp[i-1][j] + dp[i][j-1]
并设dp[i][0] = dp[0][j] = 1
根据题解思路优化,还可以只记录dp[i-1][j]和dp[i][j-1]的数据(和“互斥”的思想也有类似)。

Python 实现

(动态规划仅放原始版本,优化代码请看原链接)

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        return comb(m + n - 2, m- 1)
class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[1]*n] + [[1]+[0] * (n-1) for _ in range(m-1)]
        #print(dp)
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
        return dp[-1][-1]
        
# 作者:powcai
# 链接:https://leetcode-cn.com/problems/unique-paths/solution/dong-tai-gui-hua-by-powcai-2/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

070 爬楼梯

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/climbing-stairs

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

示例:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

(数学题,还记得是斐波那契数列)
f(n)=f(n-1)+f(n-2),对f(n),最后一步可能是一级或两级,所以将前面两项加起来得到
纯数学方法:斐波那契数列公式推导,可参考百度百科
动态规划:dp(n)=dp(n-1)+dp(n-2)
初始化dp[0]=1,dp[1]=1。

关于dp[0]的赋值,有人认为从题目n为正整数出发,不应考虑dp[0],直接dp[1] = 1,dp[2] = 2,range(3,n+2)递推,以符合dp[i]的定义。个人觉得数列本身(0),1,1,2,…,结合递推公式可以认为dp[2] = 2和dp[0]=1是等价的,顺手就行,也不必为dp[0]强行赋予意义。
关于dp[0]的其他讨论可以移步精选题解的评论区。此外,这篇还讨论了3,4,5…步的情况的扩展:https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dong-tai-gui-hua-jing-dian-a3na7/

Python实现

class Solution:
    def climbStairs(self, n: int) -> int:
        dp = [0] * (n + 1)
        dp[0] = 1;
        dp[1] = 1;
        for i in range(2,n+1):  # 注意(2,n+1)或(3,n+2)
            dp[i] = dp[i - 1] + dp[i - 2];
        return dp[n];

078 子集

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets

给你一个整数数组 nums ,返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。

示例:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

输入:nums = [0]
输出:[[],[0]]

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10

思路

回溯。题解中有两篇关于回溯题目合集https://leetcode-cn.com/problems/subsets/solution/hui-su-suan-fa-by-powcai-5/
两种做法:对每个数字,都有选、不选两种;
https://leetcode-cn.com/problems/subsets/solution/shou-hua-tu-jie-zi-ji-hui-su-fa-xiang-jie-wei-yun-/

其实回溯算法关键在于:不合适就退回上一步
然后通过约束条件, 减少时间复杂度.

回溯与递归:
深度优先搜索是递归实现的,是要搜索整个二叉树的,在这个搜索的基础上,再加点回溯/剪枝的操作就是这一类排列组合的题了,是这个关系
(官方题解下的评论https://leetcode-cn.com/problems/subsets/solution/zi-ji-by-leetcode-solution/601898)

Python实现

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        n = len(nums)
        
        def helper(i, tmp): # 辅助函数
            res.append(tmp)
            for j in range(i, n):
                helper(j + 1,tmp + [nums[j]] )
        helper(0, [])
        return res  
# 作者:powcai
#链接:https://leetcode-cn.com/problems/subsets/solution/hui-su-suan-fa-by-powcai-5/
#来源:力扣(LeetCode)
#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

补充知识

斐波那契数列第n项的求解方法:

  • n比较小的时候,可以直接使用过递归法求解,不做任何记忆化操作,时间复杂度是O(2^n),存在很多冗余计算。
  • 一般情况下,我们使用「记忆化搜索」或者「迭代」的方法,实现这个转移方程,时间复杂度和空间复杂度都可以做到O(n)。
  • 为了优化空间复杂度,我们可以不用保存f(x−2)之前的项,我们只用三个变量来维护f(x)、f(x−1)和f(x−2),你可以理解成是把「滚动数组思想」应用在了动态规划中,也可以理解成是一种递推,这样随着n的不断增大O(n)可能已经不能满足我们的需要了,我们可以用「矩阵快速幂」的方法把算法加速到O(log⁡n)。
  • 我们也可以把n代入斐波那契数列的通项公式计算结果,但是如果我们用浮点数计算来实现,可能会产生精度误差。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/climbing-stairs/solution/pa-lou-ti-by-leetcode-solution/

猜你喜欢

转载自blog.csdn.net/cosima0/article/details/112855248