hot100 -- 15.动态规划

1.爬楼梯

问题:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

方法:动态规划

f(n)=f(n-1)+f(n-2)
# 爬楼梯:动态规划
# f(n)=f(n-1)+f(n-2)   -->  每一级台阶是前两级台阶之和,以此类推,算出最终的
def ClimbStairs(n):
    if n <= 2:
        return n
    p_pre, pre, res = 1, 2, 0
    for num in range(3, n+1):
        res = p_pre + pre
        p_pre, pre = pre, res

    return res

print(ClimbStairs(5))

2.杨辉三角

问题:

给定一个非负整数 numRows生成「杨辉三角」的前 numRows 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

方法1:创建三角形 再赋值

def generate(numRows):
    tri = []
    # 创建三角形
    for i in range(1, numRows+1):
        row = []
        for j in range(i):
            row.append(1)
        tri.append(row)
    # 开始赋值
    for i in range(2, numRows):
        for j in range(1, i):
            tri[i][j] = tri[i-1][j-1] + tri[i-1][j]
    return tri

方法2:逐行动态扩展(简洁)

def generate(numRows):
    tri = []
    for i in range(numRows):
        row = [1] * (i+1)                           # 新建行
        for j in range(1, i):                       # 定在需要改的范围内
            row[j] = tri[i-1][j-1] + tri[i-1][j]    # 直接改变该行
        tri.append(row)                             # 该行加入结果
    return tri

方法3:简洁(创建复杂)

 初始化全1数组,预分配内存

# 方法1:初始化(预分配内存)
def Triangle(numRows):
    triangle = [[1 for _ in range(i)] for i in range(1, numRows+1)] # 初始化(要用循环,不能直接乘!!!)
    for i in range(2, numRows):
        for j in range(1, i):
            triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]
    return triangle

3.打家劫舍

问题:

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

方法1:动态规划(dp数组)

# 方法1:动态规划(dp数组)
def rob(nums):
    if len(nums) <= 2:
        return max(nums)
    dp = [0 for _ in range(len(nums))]

    dp[0], dp[1] = nums[0], max(nums[0], nums[1])
    for i in range(2, len(nums)):
        dp[i] = max(dp[i-2]+nums[i], dp[i-1])

    return dp[-1]

方法2:动态规划(空间优化)

# 方法2:动态规划(空间优化)
def rob(nums):
    if len(nums) <= 2:
        return max(nums)
    p_prev, prev = nums[0], max(nums[0], nums[1])
    res = 0
    for i in range(2, len(nums)):
        res = max(p_prev+nums[i], prev)
        p_prev, prev = prev, res
    return res

4.完全平方数

问题:

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

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

方法:逐点dp

从前面的点依次记录步数,到第n点时,也是最终结果。 

def numSquares(n):
    dp = [n] * (n+1)                            # 记录最少步数
    dp[0] = 0

    for i in range(1, n+1):
        j = 1
        # 遍历完全平方
        while j * j <= i:
            square = j * j
            dp[i] = min(dp[i], dp[i-square]+1)  # 更新最少步数
            j += 1
    return dp[-1]

5.零钱兑换

问题:

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

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

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

方法:逐点dp

def coinChange(coins, amount):
    dp = [float('inf')] * (amount+1)                # 设置一个很大的值,表示凑不出来
    dp[0] = 0
    for i in range(1, amount+1):                    # 逐点
        for coin in coins:                          # 遍历零钱
            if coin <= i:                           # 零钱不能超过该值
                dp[i] = min(dp[i], dp[i-coin]+1)    # 记录最少零钱数

    return dp[-1] if dp[-1] <= amount else -1       # 返回最终结果(无的话就是-1)

6.单词拆分

问题:

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

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

方法1: 逐点dp -- 逐词匹配

# 逐点dp -- 逐词匹配
# 逐个分析每个字符能否被组成
def wordBreak(s, wordDict):
    dp = [False] * (len(s)+1)
    dp[0] = True
    # word_set = set(wordDict)
    for i in range(1, len(s)+1):
        for word in wordDict:               # 逐词检查是否符合(检查:1.dp[i-L] 2.是否匹配)
            L = len(word)
            if i >= L and dp[i-L] and s[i-L:i] == word:
                dp[i] = True
    return dp[-1]

方法2: 逐点dp -- 逐点枚举

# 逐点dp -- 逐点枚举
def wordBreak(s, wordDict):
    dp = [False] * (len(s)+1)
    dp[0] = True
    word_set = set(wordDict)
    for i in range(1, len(s)+1):
        for j in range(i):                  # 逐点枚举是否符合(检查:1.dp[j] 2.是否匹配)
            if dp[j] and s[j:i] in word_set:
                dp[i] = True
    return dp[-1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_(*^▽^*)_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值