完全背包问题

题目描述

n 件物品和容量为 w 的背包,给你两个数组 weightsvalues,分别表示第 i 件物品的重量和价值,每件物品可以放入多次,求解将哪些物品装入背包可使得物品价值总和最大?

完全背包问题和01背包唯一的不同就是物品有无限个, 可以多次放入背包

建立模型

  1. 确定 dp 数组及下标的含义,数组的含义为从第 [0, i - 1] 个物品中选择物品,其重量和小于等于 j 的最大价值。
  2. 初始化 dp 数组:
    \[\begin{cases} dp[i][0] = 0 \quad (0 \le i < weights.length) \\ dp[0][j] = 0 \quad (0 \le j \le bagSize) \end{cases}
    \]
  3. 确定递推公式:
    \[\begin{cases} dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weights[i - 1]] + values[i - 1]) \quad (j \ge weights[i - 1]) \\ dp[i][j] = dp[i - 1][j] \quad (j < weights[i - 1]) \end{cases}
    \]
  4. 确定遍历顺序,外循环遍历物品 i -> 1~weights.length ,内循环遍历背包容量 j -> 1~bagSize

代码实现

public int completeBagProblem(int[] weights, int[] values, int bagSize) {
int n = weights.length;
int[][] dp = new int[n + 1][bagSize + 1]; for (int i = 1; i <= n; i++) {
for (int j = 0; j <= bagSize; j++) {
if (j >= weights[i - 1]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weights[i - 1]] + values[i - 1]);
}
else {
dp[i][j] = dp[i - 1][j];
}
}
} return dp[n][bagSize];
} /**
* 状态压缩
* 一维滚动数组实现
*/
public int completeBagProblem(int[] weights, int[] values, int bagSize) {
int[] dp = new int[bagSize + 1]; for (int i = 0; i < weights.length; i++) {
for (int j = weights[i]; j <= bagSize; j++) {
dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
}
}
}

LeeCode 377:组合总和IV

题目描述

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。顺序不同的序列被视作不同的组合。

数据范围

  • \(1 \le nums.length \le 200\)
  • \(1 \le nums[i] \le 1000\)
  • \(1 \le target \le 1000\)
  • nums 中元素互不相同

建立模型

该问题有两个关键特征:

  • 元素可以多次选择,属于完全背包问题
  • 不同的选择顺序视为不同的组合,属于排列问题

问题转化 \(\Rightarrow\) 完全背包问题

物品 \(\rightarrow\) 数组中的元素,价值和重量都是 nums[i]

背包容量 \(\rightarrow\) 目标整数

模型建立

  1. 确定 dp 数组及下标的含义,数组的含义为组合成目标整数 j 的方法数。
  2. 初始化 dp 数组,dp[0] = 1,表示组合成目标整数0只有一种方法,空集。
  3. 确定递推公式:dp[j] = dp[j] + dp[j - nums[i]]
  4. 确定遍历顺序:外循环遍历背包容量 j -> 1~target,内循环遍历物品 i -> 0 ~ nums.length - 1

为什么是外循环遍历背包容量呢?

从题目描述中分析,当前问题属于一个排列问题,即不同的添加顺序属于不同的组合。如果外循环遍历物品,添加物品时只能按照 nums 中元素的相对顺序添加,无法得到不同的顺序;反之,外循环遍历背包容量,每次循环都可以考虑所有的物品。

Example

上面的话可能不好理解,举个例子说明,nums = {1, 2, 3},target = 4。

如果外循环遍历物品,那么只会出现 {1, 3},而不会出现 {3, 1}。

如果外循环遍历背包容量,则会出现 {1, 3} 和 {3, 1}。

总结下来,可以得到以下两个结论:

  • 排列问题:外循环遍历背包容量,内循环遍历物品
  • 组合问题:外循环遍历物品,内循环遍历背包容量

代码实现

public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1; /**
* 排列问题,先遍历背包容量,后遍历物品
*/
for (int j = 1; j <= target; j++) {
for (int i = 0; i < nums.length; i++) {
if (j >= nums[i]) {
dp[j] += dp[j - nums[i]];
}
}
} return dp[target];
}

LeeCode 518:零钱兑换

题目描述

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

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0

假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带符号整数。

建立模型

问题转换 \(\Rightarrow\) 完全背包问题

物品 \(\rightarrow\) 硬币,价值和重量都是 coins[i]

背包容量 \(\rightarrow\) 总金额

该问题描述等价于从 conis 中选择若干个硬币,使其等于总金额的方法数,每个硬币可以选择多次,且不同的选择顺序视为同一方法,属于组合问题

代码实现

public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1; for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j <= amount; j++) {
dp[j] += dp[j - coins[i]];
}
} return dp[amount];
}

LeeCode 279:完全平方数

题目描述

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

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

建立模型

问题转化 \(\Rightarrow\) 完全背包问题

该问题描述等价于从 [1, 4, 9, ..., \(\lfloor \sqrt{n} \rfloor^{2}\)] 这些完全平方数中选择若干个数,使其和等于 n 的最少个数,且每个完全平方数可多次选择。

物品 \(\rightarrow\) 完全平方数

背包容量 \(\rightarrow\) 整数 n

代码实现

public int numSquares(int n) {
int[] dp = new int[n + 1];
dp[0] = 0; for (int i = 1; i <= n; i++) {
dp[i] = Integer.MAX_VALUE;
} for (int i = 1; i * i <= n; i++) {
for (int j = i * i; j <= n; j++) {
if (dp[j - i * i] != Integer.MAX_VALUE) {
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
}
}
} return dp[n];
}

LeeCode 139:单词拆分

题目描述

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s

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

建立模型

问题转化 \(\Rightarrow\) 完全背包问题

该问题描述等价于从字典中选择若干个字符串,是否可以拼接成字符串 s ,且字典中的字符串可多次使用。

物品 \(\rightarrow\) 字典中的字符串

背包容量 \(\rightarrow\) 字符串 s

模型建立

  1. 确定 dp 数组及下标含义,数组的含义为 由字符串 s 中下标 0 ~ i - 1 构成的子串是否能字典中的单词拼接。
  2. 初始化 dp 数组,\(dp[0] = true\),表示空串可由字典中的单词拼接
  3. 确定递推公式:\(dp[i] = wordDict.contains(s.substring(j, i))\ \&\&\ dp[j]\)
  4. 确定遍历顺序,枚举子串的结束位置和单词的起始位置。

代码实现

public boolean wordBreak(String s, List<String> wordDict) {
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true; // i -> 子串结束位置, j 子串起始位置
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
String temp = s.substring(j, i);
if (wordDict.contains(temp) && dp[j]) {
dp[i] = true;
}
}
} return dp[s.length()];
}

LeeCode 动态规划(三)的更多相关文章

  1. 简单动态规划——三逆数的O(N^2)解法!

    [算法]简单动态规划——三逆数的O(N^2)解法! 问题描述: 三逆数定义:给一个数的序列A[0,1,....N-1]),当i<j<k且A[i]>A[j]>A[k]时,称作ai ...

  2. 【算法】简单动态规划——三逆数的O(N^2)解法!

    问题描述: 三逆数定义:给一个数的序列A[0,1,....N-1]),当i<j<k且A[i]>A[j]>A[k]时,称作ai,aj,ak为一个三逆数. 现在给定一个长度为N的数 ...

  3. leetcode算法刷题(五)——动态规划(三)

    今天的题目不是leetcode上面的.只是觉得动态规划还是不算很熟练,就接着找了点DP的题练练 最长递增子序列的长度 题目的意思:传入一个数组,要求出它的最长递增子序列的长度.例如:如在序列1,-1, ...

  4. leecode第三百四十四题(反转字符串)

    class Solution { public: void reverseString(vector<char>& s) { int len=s.size(); char temp ...

  5. leecode第三十三题(搜索旋转排序数组)

    class Solution { public: int search(vector<int>& nums, int target) { int len=nums.size(); ...

  6. leecode第三题(无重复字符的最长子串)

    class Solution { public: int lengthOfLongestSubstring(string s) { int len=s.size(); ||len==)//边界 ret ...

  7. 动态规划(DP)

    一.基本概念 动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移.一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划. 二.基本思想与策略 基本 ...

  8. 五大常用算法之二:动态规划算法(DP)

    一.基本概念 动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移.一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划. 二.基本思想与策略 基本 ...

  9. 动态规划系列(零)—— 动态规划(Dynamic Programming)总结

    动态规划三要素:重叠⼦问题.最优⼦结构.状态转移⽅程. 动态规划的三个需要明确的点就是「状态」「选择」和「base case」,对应着回溯算法中走过的「路径」,当前的「选择列表」和「结束条件」. 某种 ...

  10. 2、动态规划接替套路框架——Go语言版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

随机推荐

  1. AUTO Uninstaller 9.3.28 最新免费密钥绿色版下载【转载】

    大家在使用autodesk系列软件之后,想要彻底卸载清除重新安装却发现无法卸载或者清除不干净的问题,这该怎么办?这里小编就给大家分享一个好用的CAD清理工具AUTO Uninstaller,轻松卸载a ...

  2. 解决IDEA输出中文乱码问题

    问题描述(中国人加油,真痛苦) 无法正确输出中文字符:(请正确分辨自己是哪一种乱码问题!) 解决方法 1.最容易想到 File -> Settings -> File Encodings下 ...

  3. printf( )和scanf( )

    printf()的转换说明 转换说明 输出 %a,%A 浮点数,十六进制数和p记数法 %c 单个字符 %d.%i 有符号的十进制整数 %e,%E 浮点数,e记数法 %f 浮点数,十进制计数法 %g/% ...

  4. maven插件实现项目一键“run maven、debug maven”等命令 => 插件名:“maven helper”

    1.在IDEA中下载插件 2.使用 总结:通过 "maven helper" 插件即可通过命令实现对项目的一键管理

  5. Abp学习(一) abp+vue +mysql框架搭建

    一.到Abp官网下载框架 地址:https://aspnetboilerplate.com/Templates 二.打开项目 修改数据库连接为MySql,默认是SQL Server 2.1.修改链接字 ...

  6. 手写 Java HashMap 核心源码

    手写 Java HashMap 核心源码 手写 Java HashMap 核心源码 上一章手写 LinkedList 核心源码,本章我们来手写 Java HashMap 的核心源码. 我们来先了解一下 ...

  7. Stm32设置串口300波特率 2400 4800 9600

    Stm32设置串口300波特率   本文以串口4为例子: 在APB1为72MHz的时钟频率下,是设置不了300波特率的,原因在于 Tx/Rx baud = fck / (16 * reg_value) ...

  8. 看看CabloyJS是如何异步加载并执行go wasm模块的

    介绍 CabloyJS提供了一个内置模块a-wasmgo,将go wasm模块的异步加载运行机制进行了封装,使我们可以非常方便的在CabloyJS项目中引入go wasm,从而支持更多的业务场景开发 ...

  9. 大量数据的mysql分页查询

    优化之前 SELECT a, b FROM c LIMIT 800000,5000 优化之后 SELECT a, b FROM c WHERE id >= ( SELECT id FROM c ...

  10. 文件包含(File Inclusion)

    什么是文件包含? 本室旨在为您提供利用文件包含漏洞的基本知识,包括本地文件包含 (LFI).远程文件包含 (RFI) 和目录遍历.此外,我们将讨论这些漏洞被发现后的风险以及所需的补救措施 在某些情况下 ...