文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。

1 0-1背包问题

背包能够承受的总重量一定w,每个物品的总量不同int[] weight表示。怎么放才能让背包中物品的总重量最大。

每次决定一种物品,要不要放入到背包中。当物品放完了或者总重量等于w,就停止放入,选择最大的总量保存下来。

2 用回溯法实现

 public class Package {private int[] weight =  new int[]{2,2,4,6,3};private int n = 5;//物品个数private int w = 9;//背包承受的最大重量private int maxW = Integer.MIN_VALUE;//结果/*** 处理第i个物品的情况,当前重量是cw* 这是回溯法,复杂度是指数级的。有些状态会计算多次。* @param i* @param cw*/public void f(int i,int cw){if(cw==w || i==n){maxW =Math.max(cw,maxW);return;}f(i+1,cw);//第i个物品,不装入背包if(cw+weight[i]<=w){f(i+1,cw+weight[i]);//第i个物品,装入背包}}public int maxWeight(){f(0,0)return maxW;}
}

我们根据上面这个特殊的例子,把回溯求解问题的递归树画出来。

递归树中的每个节点表示一个状态,用(i,cw)表示。i 表示要将要处理第i个物品,cw表示当前总重量。例如(2,2)表示我们将要处理第2个物品,在处理之前已经放入的物品总重量是2。
从递归树中能看到某些状态被重复计算了,例如f(2, 2) 和 f(3,4)被计算了两次。为了解决这个问题,可以有两种方法解决。

3 第一种:备忘录

我们可以使用备忘录,遇到状态已经计算过的就不再计算了。改进代码如下。

private boolean[][] mem = new boolean[n][w+1];/*** 记录状态,已经计算过的状态就不再计算了* @param i* @param cw*/public void fV2(int i,int cw){if(cw==w || i==n){maxW =Math.max(cw,maxW);return;}if(mem[i][cw]) return;mem[i][cw] = true;f(i+1,cw);//第i个物品,不装入背包if(cw+weight[i]<=w){f(i+1,cw+weight[i]);//第i个物品,装入背包}}

4 第二种:动态规划

我们把整个过程看做n个阶段,每个阶段只决策一种物品是否放入。每个物品决策(放或者不放)完成之后,背包中物品的重量会有多种情况。也就是说会有多种状态,对应递归树中不同的节点。

我们把每一层重复的节点合并,只记录不同的状态。基于上一层的状态集合,推导下一层集合的状态。我们合并每一层的状态,保证每一层节点个数不会超过w个。这样就避免了每一层状态节点个数指数级增长。

我们用states[n][w+1]来记录每一层可以达到的不同状态。例如上面例子中分析有(2,2)这个节点,那么states[2][2]=true。

第0个物品的重量是2,要么装入背包,要么不装入背包,决策之后会对应背包中的两种状态,背包中的总总量是0或者2.我们用state[0][0]=true,state[0][2]=true来表示这两种状态。

第1个物品的重量是2,要么装入背包,要么不装入背包,决策之后对应的背包状态:
  0+0=0
  0+2=2
  2+2=4
  这是基于上一步背包的状态计算得到的
  我们用state[1][0]=true state[1][2]=true state[1][4]=true 来表示。
  
以此类推,一直到第n-1个物品。找到state[n-1] 的 数组中找到最大的state[n-1][j]=true,返回j。

4.1 状态表

这个过程用状态表来表示,就是下图。


代码如下。代码时间复杂度O(n*w)。

public int knapsnack(int[] weight,int n,int w){boolean[][] states = new boolean[n][w+1];states[0][0] = true;if(weight[0]<w){states[0][weight[0]] = true;}for(int i=1;i<n;i++){for(int j=0;j<w;j++){if(states[i-1][j]==true){states[i][j] = true;}}for(int j=0;j<=w-weight[i];j++){if(states[i-1][j]==true){states[i][j+weight[i]] = true;}}}for(int j=w;j>=0;j--){if(states[n-1][j]) return j;}return 0;}

上面的代码实现用到二维数组。经过观察,我们发现,每次for循环里面,在计算states[i]的时候,只与states[i-1]有关系。我们应该只用一维数组就能实现。

 public int knapsnackV2(int[] weight,int n,int w){boolean[] states = new boolean[w+1];states[0] = true;if(weight[0]<w){states[weight[0]] = true;}for(int i=1;i<n;i++){//使用一维数组需要从后向前计算,否则会有多余的计算for(int j=w-weight[i];j>=0;j--){if(states[j]==true){states[j+weight[i]] = true;}}}for(int j=w;j>=0;j--){if(states[j]) return j;}return 0;}

4.2 状态方程

这道题目用状态方程来表示不太好表示。
2021-10-25:再次看这个状态方程是可以表示的。
state[i][j]=true表示当第i个物品决策完之后,背包可能的重量是j。
state[i][j]=false表示当第i个物品决策完之后,背包不可能是j。

state[i][j]=true, if state[i-1][j]=true
state[i][j+weights[i]] = true, if state[i-1][j]=true and j+weights[i]<=w(不超重)

动态规划——0-1背包问题相关推荐

  1. 动态规划——0/1背包问题(全网最细+图文解析)

    ✨动态规划--0/1背包问题(全网最细+图文解析) 作者介绍:

  2. 动态规划0—1背包问题

    动态规划0-1背包问题 Ø    问题描写叙述:    给定n种物品和一背包.物品i的重量是wi,其价值为vi,背包的容量为C.问应怎样选择装入背包的物品,使得装 入背包中物品的总价值最大? Ø   ...

  3. 背包问题动态规划matlab,01背包问题动态规划详解

    计算机算法分析考试:动态规划0-1背包问题,怎么算她说她没醉,却一直摇摇晃晃掉眼泪:你说你爱她,却从未想过给她一个家. 要考试了,老师给划重点有一题:动态规划0-1背包问题,怎么算. 怎么理问题描述: ...

  4. 0/1背包问题——动态规划方法

    1.定义 动态规划:把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解. 2.求解步骤 (1)找到状态转化条件 (2)归纳状态转移方程 (3)定义初始条件值 3.实例解析--0/1背包 ...

  5. 【动态规划】0/1背包问题

    问题 H: [动态规划]0/1背包问题 时间限制: 1 Sec  内存限制: 64 MB 提交: 152  解决: 95 [提交] [状态] [讨论版] [命题人:admin] 题目描述 张琪曼和李旭 ...

  6. 0/1背包问题——动态规划、回溯、分支限界法对比

    0/1背包问题--动态规划.回溯.分支限界法对比 2017.12.19 20:42:02 字数 3713 阅读 2820 目录 1.问题描述 1.1 问题描述 1.2 问题的数学表示(规划类问题,此种 ...

  7. 动态规划之0/1背包问题(动态规划入门)

    动态规划很早以前就接触过但是因为太晦涩难懂一下子到现在才开始真正的学习到其中的道理,0/1背包问题是动态规划的入门类问题 比较好理解 首先我们要知道动态规划是用于解决最优解的问题 它是一种思想而不是一 ...

  8. 0/1背包问题-----动态规划求解

    问题描述 有n个物品和一个容量为c的背包,从n个物品中选取装包的物品.物品i的重量为w[i],价值为p[i].一个可行的背包装载是指,装包的物品总重量不超过背包的重量.一个最佳背包装载是指,物品总价值 ...

  9. 0-1背包问题 动态规划java_C#使用动态规划解决0-1背包问题实例分析

    // 利用动态规划解决0-1背包问题 using System; using System.Collections.Generic; using System.Linq; using System.T ...

  10. 算法导论——动态规划:0-1背包问题(完全解)

    2019独角兽企业重金招聘Python工程师标准>>> package org.loda.dynamic;import org.junit.Test;/*** * @ClassNam ...

最新文章

  1. 实现人脸识别性别之路---open CV将图片显示出来
  2. C++中的override关键字
  3. linux nas解决方案_阿里产品总监:四大 Linux 支持的 NAS 解决方案
  4. Jetty 服务器架构分析(中)
  5. 90TB显存!英伟达发布新一代SuperPod超算,AI算力新巅峰!
  6. Android开发之Scroller
  7. 机器学习与计算机视觉(FPGA的图像处理方法)
  8. 没有找到MSVCP80D.dll,因此这个应用程序未能启动。重新安装应用程序...
  9. delphi 连接DBF
  10. 中国地质大学网络计算机考试试题,中国地质大学《计算机》考试题答案
  11. Python数据预处理--Gensim构建语料词典
  12. docker-compose 启动微服务项目时,nacos容器一直无限重启Restarting (1) Less than a second ago
  13. 仙剑奇侠传编年史(转自网络)
  14. java源码解析之反射(二)
  15. IDaaS | 使用 Authing + Lambda 轻松替代 AWS Cognito
  16. 通过speedtest-cli来测试网速
  17. 泛微eoffice10二开入门示例
  18. 查询练习:YEAR 与 NOW 函数
  19. 11.7 Daily Scrum(周末暂停两天Daily Scrum)
  20. 2021-08-01 大数据岗位入职系列 前传:转战大数据

热门文章

  1. 【洛谷P3389】【模板】高斯消元
  2. Spring 4 官方文档学习(十)数据访问之JDBC
  3. 接口Interface和抽象类abstract class的区别
  4. Android项目使用Eclipse进行单元测试
  5. 无插件,无com组件,利用EXCEL、WORD模板做数据导出(一)
  6. VC++6.0如何创建与调用动态链接库(dll)
  7. CSUOJ 1196- Staginner 去爬山
  8. 乘法最快的算法计算机,人类史上最快乘法算法诞生
  9. eclipse Maven项目发布到Tomcat下classes文件夹却没有子模块的编译类
  10. cesium进行模型高度测量的代码片段