Leetcode 算法面试冲刺 热题 HOT 100 刷题(300 301 309 312 322)(六十七)

300. 最长递增子序列

在这里插入图片描述
知道用动态规划,不知道怎么写,看了K神答案:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下面是根据思路,自己写的:

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [1] * n
        for i in range(1, n):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
        return max(dp)

还有一种方法,先跳过,二刷过来看。

301. 删除无效的括号

在这里插入图片描述
太难了,回头再做,先跳了

309. 最佳买卖股票时机含冷冻期

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0
        
        n = len(prices)
        # f[i][0]: 手上持有股票的最大收益
        # f[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
        # f[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益
        f = [[-prices[0], 0, 0]] + [[0] * 3 for _ in range(n - 1)]
        for i in range(1, n):
            f[i][0] = max(f[i - 1][0], f[i - 1][2] - prices[i])
            f[i][1] = f[i - 1][0] + prices[i]
            f[i][2] = max(f[i - 1][1], f[i - 1][2])
        
        return max(f[n - 1][1], f[n - 1][2])

在这里插入图片描述

下面是我的:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices or len(prices) == 0:
            return 0

        n = len(prices)

        dp = [[-prices[0], 0, 0]] + [[0] * 3 for _ in range(n - 1)]

        for i in range(1, n):
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][2] - prices[i])
            dp[i][1] = dp[i - 1][0] + prices[i]
            dp[i][2] = max(dp[i - 1][1], dp[i - 1][2])
        
        return max(dp[n -1][1], dp[n - 1][2])

312. 戳气球

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
消去型不用顺着题目去想,还是回到最后一步。只剩一个气球。那么它的子问题就是左边的区间,和右边的区间。并且这两个区间是互不影响的。

动态规划4步法:

1、确定状态

最后一步

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

子问题

在这里插入图片描述
在这里插入图片描述

2、转移方程

在这里插入图片描述

3、初始条件和边界情况

在这里插入图片描述

4、计算顺序

在这里插入图片描述
下面是我照着写的代码,这个题太难了,不会。
在这里插入图片描述

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        if not nums or len(nums) == 0:
            return 0
        A = [1] + nums + [1]
        n = len(nums)
        dp = [[0] * (n + 2) for _ in range(n + 2)]
        
        for i in range(n + 1):
            dp[i][i + 1] = 0

        for length in range(3, n + 3):
            for i in range(n + 2):
                j = i + length - 1
                if j > n + 1:
                     break
                for k in range(i + 1, j):
                    dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + A[i] * A[k] * A[j])
        
        return dp[0][n + 1]

322. 零钱兑换

在这里插入图片描述
给出不同面额的硬币以及一个总金额. 写一个方法来计算给出的总金额可以换取的最少的硬币数量. 如果已有硬币的任意组合均无法与总金额面额相等, 那么返回 -1.

在这里插入图片描述
在这里插入图片描述
直觉
在这里插入图片描述
在这里插入图片描述
所以这种题,不论我们自己怎么做设计,都会找到返例。我们该如何用动态规划的方法解决这道题呢?

动态规划4步法:

1、确定状态

是动态规划中的定海神针,类似于数学中的函数。
我们一般都需要开一个f[i] 或者 f[i][j] 的数组
确定状态需要2步:最后一步 and 子问题
在这里插入图片描述

最后一步

在这里插入图片描述
在这里插入图片描述
如上图所示,如果ak是最优的策略,俺么27-ak也一定是最优的策略。也就是27-ak也是最少能拼出的硬币数。
在这里插入图片描述
如果可以将原问题转化成一个子问题,并且规模变小了,我们就可以设置f(x)了。这里f(X)=最少用多少枚硬币拼出X。f(27)=5,最少用5枚硬币,拼出27. 所以原问题就成了求f(27)。 子问题是求f(27-ax)。

子问题

在这里插入图片描述
下面是递归的解法补充:
在这里插入图片描述
为什么要说递归的解法,在《算法通关40讲》中也提到过,DP就是递推,递推就是递归➕记忆化搜索。侯老师这里讲的其实也是这样,为什么我们要想最后一步,就是通过最后一步,知道递归的条件。
在这里插入图片描述
递归算法的问题,f(20)算了很多次,递归的问题就是重复计算非常多,导致算法的运行效率非常低下。动态规划就是可以将递归的冗余计算去掉,秘诀就是将计算结果保存下来,并且改变了计算顺序。
在这里插入图片描述

2、转移方程

刚才我们用的是()。但是在转移方程中,我们是用的[] 。表示一个数组,不是一个函数名了,是一个数组。
在这里插入图片描述
有了转移方程,我们就可以写动态规划了(其实只是解决了一大半问题)。但是我们经常忽略的问题就是初始条件和边界情况。

3、初始条件和边界情况

在这里插入图片描述
有了初始条件和边界情况,我们就要考虑计算顺序了,要先算哪个下标,再算哪个下标呢?

4、计算顺序

一般动态规划中,我们都是从小往大算。因为当我们算f[x]的时候,f[x-2], f[x-5], f[x-7]在前面已经算过了。而且不需要重复计算。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里如果是贪心算法,可能第一步就是拿7块钱,但是可能第一步就错了。这里就体现了为什么叫“动态规划”,就是在不断的从无到有,并且是在不断的规划调整的。
计算f[27],不是靠感觉,而是因为算出了f[0] —> f[26]后,自然而然的从f[25], f[22], f[20]里选择最小的(最优的)。
在这里插入图片描述

小结:

最值型动态规划
在这里插入图片描述

如何写f[x],就是在化成子问题的时候,将“最少的硬币拼出更小的面值”这句话翻译出来,f[x]表示最少用多少枚硬币拼出面值x。

动态规划的优点:消除冗余,加速计算。

看着老师的代码写的:

import sys
class Solution:

    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or len(coins) == 0 or amount < 0:
            return -1
        
        n = len(coins)
        dp = [sys.maxsize] * (amount + 1)
        dp[0] = 0

        for i in range(amount + 1):
            for j in range(n):
                last_index = i - coins[j]
                if last_index < 0 or dp[last_index] == sys.maxsize:
                    continue
                dp[i] = min(dp[i], dp[last_index] + 1)

        if dp[amount] == sys.maxsize: return -1

        return dp[amount]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值