当前位置: 首页 > news >正文

初学python的我开始Leetcode题15-2

提示:100道LeetCode热题15-2主要是动态规划相关,包括三题:完全平方数、零钱兑换、单词拆分。由于初学,所以我的代码部分仅供参考。


前言

上一次的三道动态规划是常见基本练习,这次学习三个新的题目~

粘贴题目有点黑乎乎的,试图在csdn更改格式,后来发现还不如直接粘贴方便易看,毕竟改成正文格式的话次方数还不会展示出LaTeX结果出来/(ㄒoㄒ)/~~


提示:以下是本篇文章正文内容,下面结果代码仅供参考

题目1:完全平方数

1.题目要求:

题目如下:

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,149 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:

输入:n = 12
输出:3 
解释:12 = 4 + 4 + 4

示例 2:

输入:n = 13
输出:2
解释:13 = 4 + 9

提示:

  • 1 <= n <= 10^{4}

代码框架已经提供如下:

class Solution(object):

    def numSquares(self, n):

        """

        :type n: int

        :rtype: int

        """

2.结果代码:

class Solution(object):def numSquares(self, n):""":type n: int:rtype: int"""dp = [float('inf')] * (n + 1)dp[0] = 0max_square = int(n ** 0.5)          # 兼容低版本squares = [j * j for j in range(1, max_square + 1)]for i in range(1, n + 1):for s in squares:if s > i:breakdp[i] = min(dp[i], dp[i - s] + 1)return dp[n]

说明:

1. 题目到底让我们干什么?

给你一个数字 n,问“最少”需要几个完全平方数(1、4、9、16…)加起来正好等于 n
举例:

  • n = 12 → 最少 3 个数:4 + 4 + 4

  • n = 13 → 最少 2 个数:4 + 9

2. 为什么用“动态规划”?

想象你有一张表,第 i 格写着“和为 i 的最少平方数个数”。
我们从 i = 0 开始,一格一格填到 i = n,最后第 n 格里的数字就是答案。

3. 先搭好“表”

dp = [float('inf')] * (n + 1)   # 先全部写成“无穷大”,表示暂时不知道
dp[0] = 0                       # 和为 0 当然用 0 个数
  • dp[i] 就是上面说的“第 i 格”。

  • 先把它们都设成“无穷”,后面再慢慢改小。

4. 把所有“可能用到的平方数”列出来

max_square = int(n ** 0.5)      # 计算最大平方根,向下取整
squares = [j * j for j in range(1, max_square + 1)]
  • 如果 n = 12max_square = 3,所以 squares = [1, 4, 9]

  • 这些就是后面能用的“砖块”。

5. 开始填表(核心!)

for i in range(1, n + 1):       # 从 1 填到 nfor s in squares:           # 每个 i 都试一下所有平方数if s > i:               # 平方数太大了,没必要再试breakdp[i] = min(dp[i], dp[i - s] + 1)

这句 dp[i] = min(...) 最关键:

  • 含义:如果我用了一块 s(比如 4),那剩下的 i - s(比如 12 - 4 = 8)早就算过,用了 dp[8] 个数;再加上刚用的这块 4,就是 dp[8] + 1 个数。

  • 把所有可能的 s 都试一遍,取最小值,就能更新 dp[i]

6. 答案在哪?

填完表后,dp[n] 就是和为 n 的最少平方数个数,直接返回即可:

return dp[n]

题目2:零钱兑换

1.题目要求:

题目如下:

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2^{31} - 1
  • 0 <= amount <= 10^{4}

代码框架已经提供如下:

class Solution(object):

    def coinChange(self, coins, amount):

        """

        :type coins: List[int]

        :type amount: int

        :rtype: int

        """

2.结果代码:

class Solution(object):def coinChange(self, coins, amount):""":type coins: List[int]:type amount: int:rtype: int"""# dp[i] 表示凑出金额 i 所需的最少硬币数dp = [float('inf')] * (amount + 1)dp[0] = 0  # 初始条件# 从小到大枚举金额for i in range(1, amount + 1):for c in coins:if c <= i:dp[i] = min(dp[i], dp[i - c] + 1)return dp[amount] if dp[amount] != float('inf') else -1

说明:

dp[i] 表示凑出金额 i 所需的最少硬币数。
状态转移:对于每个金额 i,枚举所有硬币 cc ≤ i),则

dp[i] = min(dp[i], dp[i - c] + 1)

初始条件:dp[0] = 0(金额 0 需要 0 枚硬币),其余置为无穷大。
若最后 dp[amount] 仍是无穷大,说明无解,返回 -1

但是,完成后发现别人有用时、内存利用更好的方法,如下:

class Solution:def coinChange(self, coins, amount) :dp = 1 << amountresult = 0while dp & 1 == 0:t = dpfor c in coins:dp |= t >> cif dp == t:return -1result += 1return result

啊w(゚Д゚)w!

这段代码其实用的是 位运算 + BFS 的思想捏...
它把“金额”映射到 一个整数的二进制位 上,从而用 一个 32/64 位整数(甚至 Python 的“大整数”)来同时记录 所有金额 的可达性,因此:

  1. 空间 极致压缩
    只用一个整数变量 dp(外加少量辅助变量),而传统数组 DP 需要 O(amount) 的整型数组。

  2. 位运算并行:
    一条 t >> c 就能一次性把 所有已可达的金额 再“向后平移 c”,相当于一次 批量 BFS
    在 CPU 的 64 位寄存器里,一次移位 / 或运算就能处理 64 个状态,常数极小。

拆解一下代码,试图用笨脑子理解中...

dp = 1 << amount        # 1. 把第 amount 位设为 1
result = 0
while dp & 1 == 0:      # 2. 只要第 0 位还是 0(金额 0 还没到达)t = dp              # 3. 记录上一轮所有可达的金额(位图)for c in coins:     # 4. 对于每种硬币dp |= t >> c    # 5. 把“当前所有可达金额 - c” 也设为可达if dp == t:         # 6. 一轮下来没有任何新金额可达 → 无解return -1result += 1         # 7. 步数 +1
return result           # 8. 第 0 位已置 1,返回步数

举个例子(amount = 11, coins = [1,2,5])

  • 初始
    dp = 1<<11 = 0b100000000000(只有 11 这一位是 1)

  • 第 1 轮(步数 1)
    用硬币 1:右移 1 → 把 10 置 1
    用硬币 2:右移 2 → 把 9 置 1
    用硬币 5:右移 5 → 把 6 置 1
    得到 dp = 0b100100100100(11,10,9,6 可达)

  • … 依次类推
    当第 0 位第一次变成 1 时,说明凑出了 0 元,此时 result 就是答案。

的确快哈:

  • cache 友好:只需要几个寄存器级操作,没有随机内存访问。

  • 并行度:一次位运算同时更新 32/64 个状态,相当于一次 向量化 BFS

  • 常数极小:没有 Python 层级的数组下标读写,移位和或运算都是 C 级指令

但是也有缺点:

  • amount 很大(> 10^5 甚至 10^6)时,Python 的大整数会变慢,内存也会膨胀。

  • 可读性较差,不如数组 DP 直观。所以懂动态规划方法也很好啦~

题目3:单词拆分

1.题目要求:

题目如下:

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。注意,你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • s 和 wordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同

代码框架已经提供如下:

class Solution(object):

    def wordBreak(self, s, wordDict):

        """

        :type s: str

        :type wordDict: List[str]

        :rtype: bool

        """

2.结果代码:

class Solution(object):def wordBreak(self, s, wordDict):""":type s: str:type wordDict: List[str]:rtype: bool"""word_set = set(wordDict)          # 用 set 加速查找n = len(s)dp = [False] * (n + 1)            # dp[i] 表示 s[:i] 能否被拼出dp[0] = True                      # 空串一定为 True# 依次考察子串结束位置 ifor i in range(1, n + 1):# 枚举最后一个单词的起始位置 jfor j in range(i):if dp[j] and s[j:i] in word_set:dp[i] = Truebreak                 # 找到一个即可提前跳出return dp[n]

说明:

dp[i] 表示 字符串 s 的前 i 个字符(即 s[:i])能否被字典中的单词拼接而成。
状态转移:枚举最后一个单词 word 的起始下标 j,只要

  • s[j:i] == word

  • dp[j] 为真,
    那么 dp[i] 就为真。

时间复杂度:O(n² * m),其中 n = len(s)m 为单词平均长度;
空间复杂度:O(n)


总结

针对动态规划的三种题型进行了学习,了解了部分有关动态规划与python的相关知识,大家加油!

http://www.dtcms.com/a/323500.html

相关文章:

  • Web自动化技术选择
  • SpringBoot日志关系
  • 【能碳建设1】用AI+开源打造物联网+能碳管理+交易SaaS系统的最短路径实施指南
  • C#:dnSpy
  • 【密码学】7. 数字签名
  • 聚众识别误报率↓78%:陌讯动态密度感知算法实战解析
  • CentOS7编译安装GCC
  • Python基础教程(六)条件判断:引爆思维Python条件判断的九层境界
  • Java Stream流详解:用法与常用API实战
  • Kotlin 协程线程切换机制详解
  • 规划是自动驾驶的“头脑”
  • 灰度测试(灰度发布、金丝雀发布)
  • 推荐系统学习笔记(十一)预估分数融合
  • leetcode-C语言-3479.水果成篮 III
  • C++ vector类
  • 3.2Vue Router路由导航
  • 【熵增与熵减:从混乱到有序的奥秘】
  • 词向量可视化:用TensorBoard或PCA探索词向量空间
  • 【JavaEE】(11) 前端基础三件套
  • 大数据与财务管理:未来就业的黄金赛道
  • java9学习笔记-part2
  • rosrun 和 roslaunch 区别
  • Busybox编译、制作initramfs,并在QEMU中运行
  • 医疗健康Agent:诊断辅助与患者管理的AI解决方案
  • rotary_emb 位置编码 加速
  • 练习uart和摄像头内核驱动开发测试
  • imx6ull-驱动开发篇15——linux自旋锁
  • 2025-08-09 李沐深度学习14——经典卷积神经网络 (2)
  • 【C++】模版进阶
  • redis存储原理与数据模型