动态规划算法问题整理

动态规划问题真是属于那种刷了忘忘了刷啊

背包问题概述

  1. 0-1 背包问题:每种物品只能选择放入或不放入背包一次。
  2. 完全背包问题:每种物品可以无限次选择放入背包。
  3. 多重背包问题:每种物品有限定的数量,可以选择放入背包多次,但不超过这个数量。
  4. 分组背包问题:物品被分为若干组,每组中的物品互斥,即从每组中最多只能选一个放入背包。

经典0/1背包问题

题目

已知重量和价值的 n 件物品,以及一个用来装这些物品的背包。背包不能装载超过一定最大重量的物品。

需要使背包中物品的总价值最大化,同时确保所选物品的重量之和不超过背包的容量。

如果没有在容量限制范围内的组合方式则返回0。

思路

初始化动态数组,需要注意多初始一行一列,为“真正的”第一行第一列生成数据做准备

对于此题目的动态数组,dp[i][w]的含义为考虑前i个物品,在背包容量不超过w的条件下可以获得的最大价值,也就是说列代表了容量,一直变大,行代表了商品,一行一个商品

  • 遍历过程中,我们需要考虑到前i个物品,容量为w,如果i物品的相应容量比我们的w小或者相等,证明可以装下
  • 不加入物品 i 到背包:不把物品加入背包,背包的最大价值要继承dp数组里i-1(不考虑第i个物品)行,w(容量还是w大小)列的值。

  • 加入物品 i 到背包:考虑第i个物品,则需要考虑i-1个物品在背包容量为w-i的重量时所获的最大值以及第i个物品价值作为这个单元格的值。

  • 最后返回最右下角的值,这里是在整个背包容量下考虑所有物品放入背包的最大价值。

代码

def knapsack(values, weights, capacity):
    n = len(values)
    dp = [[0 for _ in range(capacity + 1)] for _ in range(n + 1)]

    for i in range(1, n + 1):
        for w in range(1, capacity + 1):
            if weights[i-1] <= w:
                # 如果当前物品可以加入背包
                dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1])
            else:
                # 如果当前物品不能加入背包
                dp[i][w] = dp[i-1][w]

    return dp[n][capacity]

完全背包问题 leetcode322题

题目

给你一个整数数组 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] <= 231 - 1
  • 0 <= amount <= 104

思路

初始化动态数组,这里只要看总和为i的物品总价值怎么凑,所以动态数组就一行,同样多初始化一列,需要注意第一列为0容易理解,即凑0元钱不需要任何物品,其他的动态数组值为了后面代码方便统一初始化为一个较大的值

遍历i,i代表了需要凑的总价值,对于所有的coin面值,如果i比coin大或者相等,此时i这里的值要进行更新

更新为此时现有的凑法硬币数量,以及价值i减去此coin值凑法硬币数量两者硬币最小的那种方式的硬币数量

最后返回时检查需要凑的价值哪个单元格值是否小于amount + 1(我们为动态数组设置的初始值),如果小于,则证明有解,返回dp数组中这个解即可

补充说明

当硬币不能正好凑成amount数,但是可以凑的大于amount数的情况,dp数组如下

可以得知我们的方法有效,如果dp[amount]有值,证明amount确实是可以被凑成的

代码

此题目的动态数组由于只有一个需要考虑的维度

def coinChange(coins, amount):
    dp = [amount + 1] * (amount + 1)
    dp[0] = 0
    
    for i in range(1, amount + 1):
        for coin in coins:
            if i - coin >= 0:
                dp[i] = min(dp[i], dp[i - coin] + 1)

    return dp[amount] if dp[amount] < amount + 1 else -1

较为特殊的完全背包问题 leetcode139题

题目

给你一个字符串 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

思路

本题也可以回溯法解决

初始化dp数组,多初始化一列,返回的是True和False,所以让除第一个元素以外所有元素都为False

如果dp[j]可以被组合成功且从j到i部分的字符串在wordDict内,则可以让dp[i]为True,因为可以被组合得到

代码

class Solution(object):
    def wordBreak(self, s, wordDict):
        dp = [False] * (len(s) + 1)
        dp[0] = True

        for i in range(1, len(s) + 1):
            for j in range(i):
                if dp[j] and s[j:i] in wordDict:
                    dp[i] = True
                    break
        
        return dp[len(s)]

1143. 最长公共子序列

题目

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1 和 text2 仅由小写英文字符组成。

思路

老样子初始化,老样子遍历

dp构造依据

  1. text1[i-1] == text2[j-1] 时:这个字符一定属于两个字符串的公共子序列。

  2. text1[i-1] != text2[j-1] 时:最长公共子序列要么在 text1 的前 i-1 个字符与 text2 的前 j 个字符之间,要么在 text1 的前 i 个字符与 text2 的前 j-1 个字符之间。

  3. 要注意text1[i-1] == text2[j-1]时,dp[i][j]直接 = dp[i-1][j-1] + 1 

代码

class Solution(object):
    def longestCommonSubsequence(self, text1, text2):
        m, n = len(text1), len(text2)
        dp = [[0] * (n + 1) for _ in range(m + 1)]

        for i in range(1, m+1):
            for j in range(1, n+1):
                if text1[i-1] == text2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        return dp[m][n]

相关推荐

  1. 算法动态规划(dp问题),持续更新

    2024-03-25 09:08:03       41 阅读
  2. 动态规划算法解决背包问题(Python)

    2024-03-25 09:08:03       39 阅读
  3. 算法 & 动态规划 &路径问题】二维dp问题

    2024-03-25 09:08:03       15 阅读
  4. 算法学习:动态规划之爬楼梯问题

    2024-03-25 09:08:03       41 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-25 09:08:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-25 09:08:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-25 09:08:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-25 09:08:03       20 阅读

热门阅读

  1. 滴滴基于 Clickhouse 构建新一代日志存储系统

    2024-03-25 09:08:03       21 阅读
  2. 精读《如何做好 CodeReview》

    2024-03-25 09:08:03       19 阅读
  3. 复习Day2_

    2024-03-25 09:08:03       18 阅读
  4. TCP重传机制详解——03DSACK

    2024-03-25 09:08:03       17 阅读
  5. 【boost_search搜索引擎】2.正排索引和倒排索引

    2024-03-25 09:08:03       16 阅读
  6. P1873 [COCI 2011/2012 #5] EKO / 砍树

    2024-03-25 09:08:03       17 阅读
  7. AI大模型的训练与优化

    2024-03-25 09:08:03       16 阅读
  8. 第三十二章 配置服务器访问

    2024-03-25 09:08:03       19 阅读
  9. 大数据实时计算的Windows功能?

    2024-03-25 09:08:03       19 阅读