分支限界法和之前讲的回溯法有一点相似,两者都是在问题的解的空间上搜索问题的解。但是两者还是有一些区别的,回溯法是求解在解的空间中的满足的所有解,分支限界法则是求解一个最大解或最小解。这样,两者在解这一方面还是有一些不同的。之前回溯法讲了N后问题,这个问题也是对于这有多个解,但是今天讲的01背包问题是只有一个解的。下面就讲讲分支限界法的基本思想。

分支限界法常以广度优先或以最小消耗(最大效益)优先的方式搜索问题的解空间树。问题的解空间树是表示问题解空间的一颗有序树,常见的有子集树和排列树。分支限界法和回溯法的区别还有一点,它们对于当前扩展结点所采用的扩展方式也是不相同的。分支限界法中,对于每一个活结点只有一次机会成为扩展结点。活结点一旦成为了扩展结点,就一次性产生其所有的子结点,子结点中,不符合要求的和非最优解的子结点将会被舍弃,剩下的子结点将加入到活结点表中。再重复上面的过程,直到没有活结点表中没有结点,至此完成解决问题的目的。

分支限界法大致的思想就是上面的叙述,现在就可以发现,对于结点的扩展将会成为分支限界法的主要核心。所以,分支限界法常见的有两种扩展结点的方式,1.队列式(FIFO)分支限界法,2.优先队列式分支限界法。两种方法的区别就是对于活结点表中的取出结点的方式不同,第一种方法是先进先出的方式,第二种是按优先级取出结点的方式。两中方法的区别下面也会提到。

在背包问题中还会提到一个子树上界的概念,其实就是回溯法中的剪枝函数,只不过,分支限界法里的剪枝函数改进了一些,剪枝函数同样也是分支限界法里比较重要的东西。

下面就讲一讲01背包问题的实现。01背包问题和前面讲的背包问题的区别不大,就是01背包问题的物品不可以只放入部分,01背包问题的物品只能放入和不放入两个选择,这也是名字中01的原因。其他的和背包问题相差不大,这里也不再累述。

算法的主体是比较容易想的,首先,将数据进行处理,这也是上面讲到的第二种取结点的方式(优先队列式)。因为要给每个物品设置优先级,这里将价值作为优先级显然不够好,就想到了将价值与重量的比值(权重值)作为优先级。要写一个排序算法,将物品数组中物品按权重值排序。下面就要想一下子树上界函数了,这里上界的函数借鉴了一下背包问题的结局方案,因为子树的最大值一定小于非01背包问题的最优解,所以用到了之前背包问题的代码,将代码进行了处理,需要传入参数,也要有返回值,这里就不再重复叙述了。

下面就是代码的主体,这一部分我想了大概两个星期,一开始的思路出现了问题,总是想着使用数组来实现算法的主体,同时在使用递归来循环遍历数组,后来发现,这样做就和回溯法一模一样,后来借鉴了一下网上的代码,将自己的主体代码改了改,多写了一个类(结点类),剩下的就比较好实现了,将初始结点添加到结点表中,找到左节点和右节点,同时利用函数判断结点是否符合要求,在将左节点和右节点添加到结点表中,在循环遍历结点表,知道结点表中没有活结点,一直重复这个步骤。在左节点的判断中,同时还要判断左节点的值是不是大于最优解,算法的大部分都是在判断。算法主体就结束了。

再来讲一讲算法的构建最优解,这个我也是想不出来,老师最后提醒了我,先是将结点添加到另一个节点表中,在问题的结束后,遍历找到最优解的父节点,判断父节点是不是左节点,在重复这个步骤,直到没有父节点。完成后将左节点的索引标记为放入背包中,这样就完成了最优解的构建。剩下的问题就是一些细节问题了。

代码如下:

package sf;

import java.util.LinkedList;

import java.util.Scanner;

/*

* 01背包问题,分支限界法

*/

public class demo8 {

/*

* 主方法

*/

public static void main(String[] args) {

//输入数据

System.out.println("请输入背包的容量w和物品的个数n");

Scanner reader = new Scanner(System.in);

int w = reader.nextInt();// 背包的容量

int n = reader.nextInt();// 物品的个数

int solution=-1;

BLBag[] p = new BLBag[n];

System.out.println("请依次输入各个物品的重量w和价值v和名称s");

int weigth;

int value;

String pid;

for (int i = 0; i < n; i++) {

pid = reader.next();

weigth = reader.nextInt();

value = reader.nextInt();

p[i] = new BLBag(pid, weigth, value);

}

// 输入数据结束

/*

* 数据

* 001 16 45 002 15 25 003 15 25

*/

// 算法开始

//声明状态数组并初始化为空

Integer[] a=new Integer[n];

for(int i=0;i

//对p数组按权重排序

sort(p);

//打印结果

int haha=branchandlimit(p, w, a, solution);

System.out.println("最优解为:"+haha);

}

/*

* 权重排序,选择排序

*/

public static void sort(BLBag[] p) {

BLBag t;

for (int i = 0; i < p.length; i++) {

int max = i;

t = p[i];

for (int j = i; j < p.length; j++) {

if (t.wi < p[j].wi) {

t = p[j];

max = j;

}

}

t = p[i];

p[i] = p[max];

p[max] = t;

}

}

/*

* 求上界的函数 数组p 当前位置 当前背包重量 返回是最大价值(不包含背包的已有价值)

*/

public static double findbound(BLBag[] p,int i,int weight)

{

double value = 0;

//将状态位后面的物品求贪心算法的解,上界函数的解为返回值+当前背包价值

forLOOP:for(int k=i;k

{

//贪心算法求解问题(修改版)

if(p[k].weight

value=value+p[k].value;

weight=weight-p[k].weight;

}else{

double a=weight*p[k].wi;//当前价值

value=value+a;

weight=0;

break forLOOP;//跳出循环

}

}

return value;

}

/*

* 分支限界法主体 参数分别为物品数组p,重量,价值,状态数组,当前考虑位置i ,最优解

*/

public static int branchandlimit(BLBag[] p,int weight,Integer[] a,double solution)

{

//声明队列

LinkedList nodelist=new LinkedList();

LinkedList nodesolution=new LinkedList();

nodelist.add(new Node(0, 0, 0));

nodesolution.add(new Node(0,0,0));

while(!nodelist.isEmpty())

{

//取出元素

Node node = nodelist.pop();

//判断条件,节点的不放入的最大值大于当前最优解,节点小于数组的长度

//这里不用等于,必须要大于

if(node.getUnbounvalue()+node.getCurrvalue()>solution && node.getIndex()

{

//左节点

int leftWeight=node.getCurrweight()+p[node.getIndex()].weight;

int leftvalue=node.getCurrvalue()+p[node.getIndex()].value;

Node left=new Node(leftWeight, leftvalue, node.getIndex()+1);

//设置左节点的父节点

left.setFather(node);

left.setIsleft(true);

//将左节点添加到最优解队列中

nodesolution.add(left);

//设置左节点的上界价值

left.setUnbounvalue((int)findbound(p, node.getIndex(), weight-node.getCurrweight()));

//左节点的重量小于等于背包的承重,且左节点的上界价值大于最优解

if(left.getCurrweight()<=weight && left.getUnbounvalue()+left.getCurrvalue()>solution)

{

//将节点加入队列中

nodelist.add(left);

a[node.getIndex()]=1;

//将最优值重新赋值 条件就是节点的当前价值大于问题的最优解

if(left.getCurrvalue()>solution)

{

solution=left.getCurrvalue();

//System.out.println("放入的物品有:"+p[node.getIndex()].pid);

}

}

//右节点 右节点的设置不需要太多,和父节点差不多

Node right=new Node(node.getCurrweight(), node.getCurrvalue(), node.getIndex()+1);

//将右节点添加到最优解队列中

right.setFather(node);

right.setIsleft(false);

nodesolution.add(right);

right.setUnbounvalue((int)findbound(p,node.getIndex(),weight-node.getCurrweight()));

//右节点的上界价值大于当前最优解

if(right.getUnbounvalue()+node.getCurrvalue()>solution)

{

//添加右节点

nodelist.add(right);

a[node.getIndex()]=0;

}

}

}

/*

* 调用最优解方法

*/

pr(nodesolution,(int)solution,p);

//返回最优解

return (int) solution;

}

/**

*

* @Description: 求解最优解的方法

* @param @param nodesolution

* @return void

* @throws

* @author yanyu

* @date 2018年5月21日

*/

//参数为

public static void pr(LinkedList nodesolution,int solution,BLBag[] p)

{

int[] a=new int[p.length];

Node prnode=null;

//从list中循环遍历最优解的节点

for(Node node:nodesolution)

{

if(node.getCurrvalue()==solution){

//System.out.println("最优解的父节点的索引为:"+node.getFather().getIndex());

prnode=node;

}

}

//循环遍历最优节点的父节点,判断其是否为左节点

while (prnode.getFather()!=null)

{

if(prnode.isIsleft())

{

a[prnode.getIndex()-1]=1;

}

prnode=prnode.getFather();

}

//打印

for(int i=0;i

{

if(a[i]==1) System.out.println("放入了物品:"+p[i].pid);

}

}

}

/*

* 背包类

*/

class BLBag {

public int weight;// 重量

public int value;// 价值

public double wi;// 权重

public String pid;// 背包名称

public BLBag(String pid, int weight, int value) {

this.weight = weight;

this.value = value;

this.pid = pid;

this.wi = (double) value / weight;

}

}

/**

*

* ClassName: Node

* @Description: 节点类

* @author yanyu

* @date 2018年5月17日

*/

class Node

{

//当前物品的属性

private int currweight;//当前重量

private int currvalue;//当前价值

private int unbounvalue;//上界价值

private int index;//索引

private Node father;//父节点

private boolean isleft;//是否为左节点

public boolean isIsleft() {

return isleft;

}

public void setIsleft(boolean isleft) {

this.isleft = isleft;

}

public Node getFather() {

return father;

}

public void setFather(Node father) {

this.father = father;

}

public int getCurrweight() {

return currweight;

}

public void setCurrweight(int currweight) {

this.currweight = currweight;

}

public int getCurrvalue() {

return currvalue;

}

public void setCurrvalue(int currvalue) {

this.currvalue = currvalue;

}

public int getUnbounvalue() {

return unbounvalue;

}

public void setUnbounvalue(int unbounvalue) {

this.unbounvalue = unbounvalue;

}

public int getIndex() {

return index;

}

public void setIndex(int index) {

this.index = index;

}

//构造函数

public Node(int currweight,int currvalue,int index)

{

this.currweight=currweight;

this.currvalue=currvalue;

this.index=index;

}

}

上面就是代码,下面说一说具体的细节,队列中添加结点的方式是先进先出,这里将左节点放到前面就是将优先的结点先出,其中每个判断的判断条件都是要注意的点,还有就是结点的索引要注意和物品数组中下标相差一,这个要注意。要不然构建最优解的时候会越界,其他的一些细节都写在注释了里。不再累述。

下面讲一讲自己的一些想法,对于自己一开始想用数组实现的想法,我倒现在还是认为是对的,因为看到现在,算法是实现了,但是估计效率不是很高,我估计没有数组实现的方式效率高,这里因为能力有限,只能放弃数组的实现方式。也是算法下一步进步的方式吧。

结束。

分支限界法 01背包 java_分支限界法解决01背包问题相关推荐

  1. NOJ--宠物小精灵之收服(01背包,二维费用背包问题)

    宠物小精灵之收服 1000ms  65536K Description: 宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事. 一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵 ...

  2. 动态规划之背包问题——01背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

  3. 动态规划 背包问题小结 0-1背包(采药 九度第101题) 完全背包(Piggy-Bank POJ 1384) 多重背包(珍惜现在,感恩生活 九度第103题)

    本小结介绍0-1背包.完全背包以及多重背包问题 记忆要点: 0-1背包:二维数组情况下,顺序遍历体积或者倒序均可以                降维情况下需倒序遍历体积 完全背包:数组降维+顺序遍历 ...

  4. c语言 用回溯算法解决01背包问题,回溯法解决01背包问题

    <回溯法解决01背包问题>由会员分享,可在线阅读,更多相关<回溯法解决01背包问题(21页珍藏版)>请在人人文库网上搜索. 1.回溯法解决01背包问题,回溯法解决01背包问题, ...

  5. 【python】一篇讲透背包问题(01背包 完全背包 多重背包 二维费用背包)

    面对背包问题,有一个很重要的方程式:状态转移方程式 所以每一种背包问题我都会给出状态转移方程式 #01背包 什么是01背包型问题? 先给大家感受一下01背包型问题: 给定n种物品和一背包.物品i的重量 ...

  6. 01背包,完全背包,多重背包,混合背包,二维费用背包,分组背包,背包问题求方案数

    1 01背包问题 有 NNN 件物品和一个容量是 VVV 的背包.每件物品只能使用 一次. 第 iii 件物品的体积是 viv_ivi​,价值是 wiw_iwi​.求解将哪些物品装入背包,可使这些物品 ...

  7. 【恋上数据结构】动态规划(找零钱、最大连续子序列和、最长上升子序列、最长公共子序列、最长公共子串、0-1背包)

    动态规划(Dynamic Programming) 练习1:找零钱 找零钱 - 暴力递归 找零钱 - 记忆化搜索 找零钱 - 递推 思考题:输出找零钱的具体方案(具体是用了哪些面值的硬币) 找零钱 - ...

  8. 动态规划——01背包

    动态规划--01背包 1. 经典"01背包" 2. "01背包"方法归纳 3. 实战 3.1 分割等和子集 3.2 最后一块石头的重量 II 3.3 目标和 3 ...

  9. HDU2546_用01背包做

    题目大意:          电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法 ...

最新文章

  1. 语音识别:时间序列的匹配算法(Needleman-Wunsch 算法)
  2. python打包exe 之打包sklearn模型中的各种坑及其解决方法。
  3. ajax 请求调用问题
  4. 安卓TTS语音合成经验分享(科大讯飞语音+)集成
  5. ECMAScript 6的解构赋值 ( destructuring assignment)
  6. rs485如何使用_12个经典问答:带你全面了解RS485接口知识
  7. ERP CRM与SCM整合过程中的知识转移
  8. 使用TestContainers进行数据库测试
  9. linux sybase 自动备份,Linux平台下Sybase数据库备份方法分析.doc
  10. java math max_Java Math类静态double max(double d1,double d2)示例
  11. [c++基础] const char and static const char
  12. [转] 数据库加锁 sql加锁的
  13. 【JUC】JDK1.8源码分析之ReentrantReadWriteLock(七)
  14. UCI机器学习数据库
  15. Cisco RV180W 路由器设置
  16. syzlang语法编写案例学习 —— Looking for Remote Code Execution bugs in the Linux kernel
  17. 手机显示服务器迁移中是什么意思,服务器迁移注意什么?什么是服务器迁移?...
  18. Oracle 数据库学习
  19. 物联网之STM32开发一(基础知识)
  20. 全志F1C200s芯片处理器参数介绍

热门文章

  1. Oracle 两字段连接
  2. AI美少女小冰单飞,自由成长成现实?
  3. QQ空间迁移_【联想V3000笔记本去白名单之二BIOS刷入】
  4. 打击犯罪 解题报告
  5. 记录研究截取QQ密码的几点心得
  6. 剑网3一直连接不上服务器,win7系统剑网3连不上服务器的解决方法
  7. 【QT开发笔记-基础篇】| 第二章 常用控件 | 2.9 滑动条 QSlider
  8. Python:实现max sub array最大子数组算法(附完整源码)
  9. uniapp 字符串转数组
  10. CEF无法加载flash的问题