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

thinksns网站开发wordpress微信注册登录界面

thinksns网站开发,wordpress微信注册登录界面,泰州网络营销,提高网站排名怎么做目录 什么是背包问题? 动态规划问题的一般解决办法: 0-1背包问题: 0 - 1背包类问题 分割等和子集: 完全背包问题: 完全背包类问题 零钱兑换II: 什么是背包问题? 背包问题(Knapsack problem)是一种…

目录

什么是背包问题?

动态规划问题的一般解决办法:

0-1背包问题:

0 - 1背包类问题  分割等和子集: 

完全背包问题: 

完全背包类问题 零钱兑换II:


什么是背包问题?

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年由Merkle和Hellman提出的。

动态规划问题的一般解决办法:

动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维数组或者二维数组来保存。下面我们先来讲下做动态规划题很重要的三个步骤:

  • 🧐 步骤一:定义dp数组元素的含义
  • 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)
  • 🧐第三步骤:找出初始值(base case)

接下来的题目我们会按照这三个步骤来解释说明

前言:本文包含动态规划中的经典背包问题,有关背包问题的描述如下:

在动态规划中,背包问题是一个经典的优化问题,它可以分为0-1背包问题和完全背包问题两种类型。下面我们就来看看这两个问题:

0-1背包问题:

问题描述:

给你一个可装载重量为 W 的背包和 N 个物品,每个物品有重量和价值两个属性。其中第 i 个物品的重量为 wt[i],价值为 val[i]。现在让你用这个背包装物品,每个物品只能用一次,在不超过被包容量的前提下,最多能装的价值是多少?

  • 🧐 步骤一:定义dp数组元素的含义:

由于状态有两个,就是「背包的容量」和「可选择的物品」,这里我们就需要用到一个二维的dp

数组,如下为dp数组的定义:

🦉🦉🦉dp[i][w] 的定义如下:对于前 i 个物品,当前背包的容量为 w,这种情况下可以装的最大价值是 dp[i][w]

  • 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)

1.如果你没有把这第 i 个物品装入背包,那么很显然,最大价值 dp[i][w] 应该等于 dp[i-1][w],继承之前的结果(翻译一下就是不装入第i个物品,相当于对前 i - 1 个物品进行选择,对应此时的背包容量w)。即此时的状态转移方程是:dp[ i ][ w ] = dp[ i - 1 ][ w ]

2.如果你把这第 i 个物品装入了背包,此时背包剩余容量为 w - wt[ i - 1 ](wt数组下标是从0开始的, wt[ i - 1 ] 相当于第 i 个物品的重量,val 也一样)

则此时的状态转移方程是:dp[ i ][ w ] = dp[ i - 1 ][ w - wt[ i - 1] ] + val[ i - 1 ]

  • 🧐第三步骤:找出初始值(base case):

这题的base case 相对简单,当物品个数为0或则背包当前容量为0时,dp[ i ][ w ] 都等于0

按照上述的状态转移方程,我们可以填出对应dp表格(以图中的例子为例):

有了上述铺垫后,动态规划的代码就很好实现了,具体代码如下:

int knapsack(int W, int N, int[] wt, int[] val) {assert N == wt.length;// base case 已初始化,数组自动全部初始化为0int[][] dp = new int[N + 1][W + 1];for (int i = 1; i <= N; i++) {for (int w = 1; w <= W; w++) {if (w - wt[i - 1] < 0) {// 这种情况下只能选择不装入背包dp[i][w] = dp[i - 1][w];} else {// 装入或者不装入背包,择优dp[i][w] = Math.max(dp[i - 1][w - wt[i-1]] + val[i-1], dp[i - 1][w]);}}}return dp[N][W];
}

有了上面的一定了解后,我们来看看0 - 1背包的类似题:

0 - 1背包类问题  分割等和子集: 

看一下力扣第 416 题「分割等和子集open in new window」:

题目描述:输入一个只包含正整数的非空数组 nums,请你写一个算法,判断这个数组是否可以被分割成两个子集,使得两个子集的元素和相等。对应函数签名如下:

我们可以将这个问题转化为0 - 1背包问题,具体做法:

这个问题相当于给一个可装载重量为 sum / 2 的背包和 N 个物品,每个物品的重量为 nums[i]。现在让你装物品,是否存在一种装法,能够恰好将背包装满?按照上述解题思路就是:

  • 🧐 步骤一:定义dp数组元素的含义:

dp[i][j] = x 表示,对于前 i 个物品(i 从 1 开始计数),当前背包的容量为 j 时,若 x 为 true,则说明可以恰好将背包装满,若 x 为 false,则说明不能恰好将背包装满

  • 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)

与上面类似,这里就直接给出来了:

1.不把这第 i 个物品装入背包:dp[ i ][ j ] = dp[ i - 1 ][ j ]

2.把这第i个物品装入背包:dp[ i ][ j ] = dp[ i - 1][ j - nums[ i - 1 ] ]

  • 🧐第三步骤:找出初始值(base case):

当背包容量为0时(sum / 2= 0)这时无论物体有多少个都可以满足条件,就是什么都不装嘛

ok,接下来看完整代码:

boolean canPartition(int[] nums) {int sum = 0;for (int num : nums) sum += num;// 和为奇数时,不可能划分成两个和相等的集合if (sum % 2 != 0) return false;int n = nums.length;sum = sum / 2;boolean[][] dp = new boolean[n + 1][sum + 1];// base casefor (int i = 0; i <= n; i++)dp[i][0] = true;for (int i = 1; i <= n; i++) {for (int j = 1; j <= sum; j++) {if (j - nums[i - 1] < 0) {// 背包容量不足,不能装入第 i 个物品dp[i][j] = dp[i - 1][j];} else {// 装入或不装入背包dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];}}}return dp[n][sum];
}

完全背包问题: 

完全背包问题与0-1背包问题类似,但不同之处在于每个物品可以选择放入背包多次(数量无限),即每个物品的选择是一个无限的选择。我们给出对应的解题方法:

  • 🧐 步骤一:定义dp数组元素的含义:

若只使用前 i 个物品(可以重复使用),当背包容量为 j 时,能装入背包的最大价值为dp[i][w] 

  • 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)

1.不将第i个物品装入背包,此时:dp[ i ][ w ] = dp[ i - 1 ][ w ]

2.将第i个物品装入背包,此时:dp[ i ][ w ] = dp[ i ][ w -wt[ i - 1] ] + val[ i - 1 ] 

  • 🧐第三步骤:找出初始值(base case):

这题与0 - 1的bas背包的base case 一致,当物品个数为0或者背包当前容量为0时,dp[ i ][ w ] 都等于0

对应的动态规划代码为:

int fullBackpack(int W, int N, int[] wt, int[] val) {assert N == wt.length;// base case 已初始化,数组自动全部初始化为0int[][] dp = new int[N + 1][W + 1];for (int i = 1; i <= N; i++) {for (int w = 1; w <= W; w++) {if (w - wt[i - 1] < 0) {// 这种情况下只能选择不装入背包dp[i][w] = dp[i - 1][w];} else {// 装入或者不装入背包,择优dp[i][w] = Math.max(dp[i][w - wt[i-1]] + val[i-1], dp[i - 1][w]);}}}return dp[N][W];
}

完全背包类问题 零钱兑换II:

这是力扣第 518 题「零钱兑换 IIopen in new window」,题目描述:

给定不同面额的硬币 coins 和一个总金额 amount,写一个函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。对应函数签名如下:

我们可以把这个问题转化为完全背包问题的描述形式

有一个背包,最大容量为 amount,有一系列物品 coins,每个物品的重量为 coins[i]每个物品的数量无限。请问有多少种方法,能够把背包恰好装满?按照动态规划三步走:

  • 🧐 步骤一:定义dp数组元素的含义:

dp[i][j] 的定义如下:若只使用前 i 个物品(可以重复使用),当背包容量为 j 时,有 dp[i][j] 种方法可以装满背包。

  • 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)

1.如果你不把这第 i 个物品装入背包,也就是说你不使用 coins[i-1] 这个面值的硬币,那么凑出面额 j 的方法数 dp[i][j] 应该等于 dp[i-1][j],继承之前的结果

即状态转移方程为:dp[ i ][ j ] = dp[ i -1 ][ j ]

2.如果你把这第 i 个物品装入了背包,也就是说你使用 coins[i-1] 这个面值的硬币,那么 dp[i][j] 应该等于 dp[i][j-coins[i-1]]

即状态转移方程为:dp[ i ][ j ] = dp[ i ][ j - coins[ i - 1] ]

  • 🧐第三步骤:找出初始值(base case):

base case 为 dp[0][..] = 0, dp[..][0] = 1i = 0 代表不使用任何硬币面值,这种情况下显然无法凑出任何金额;j = 0 代表需要凑出的目标金额为 0,那么什么都不做就是唯一的一种凑法。

写出动态规划代码:

int change(int amount, int[] coins) {int n = coins.length;int[][] dp = int[n + 1][amount + 1];// base casefor (int i = 0; i <= n; i++) dp[i][0] = 1;for (int i = 1; i <= n; i++) {for (int j = 1; j <= amount; j++)//装入背包或者不装入,加起来if (j - coins[i-1] >= 0)dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i-1]];else // < 0 只能选择不装入背包dp[i][j] = dp[i - 1][j];}return dp[n][amount];
}

结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固自己的知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!

http://www.15wanjia.com/news/185116.html

相关文章:

  • 微网站建设找哪家网页设计模板套用步骤
  • 关于建设工程招标的网站wordpress ios使用
  • 网站购物车实现快速建企业网站
  • 网站建设的空间指的是做程序的网站
  • 装饰公司网站开发境外网址app
  • 网站支付可以做二清网站上线多少钱
  • WordPress建站维护服务wordpress 首页链接
  • 建立网站并以此为基础从事经营活动的企业称为什么桂林漓江门票多少钱一张
  • 现在用什么工具建网站织梦网站图片代码
  • 网站备案 广东营销策略的思路
  • 国税政务公开网站建设做网站建设的工资高吗
  • 新网站如何做优化网上接单做衣服哪个网站
  • 重庆自助建站网站建站公司哪家好
  • 李氏牛仔网站建设风格安徽定制小程序开发
  • 本人已履行网站备案信息百度指数免费查询
  • 教做鞋的网站政务公开网站建设重点
  • 网站用免费空间好不好成都发现1例新冠本土病例
  • 网站推广线上推广手机网站建立免费平台
  • 湖北企业模板建站开发网站有哪些布局
  • 求一个全部用div做的网站个人网站怎么做代码
  • 百度基木鱼建站网店推广软件
  • 西安本地十家做网站建设的公司做文案应该关注的网站推荐
  • 西安找公司建网站谷歌排名优化
  • 成都哪个网站建设比较好网页设计与制作课程评价
  • 酒店网站设计公司网络推广方案怎么写模板
  • 做网站深圳影视自助建站官网
  • 贵阳网站推广¥做下拉去118cr腾讯云网站备案吗
  • 学ui可以做网站么环保类网站模板
  • 什么是网站建设中的目录通江网站建设
  • 微信公众号的微网站怎么做苏州建设信息网