分支限界法 01背包 java_分支限界法解决01背包问题

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

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

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

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

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

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

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

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

代码如下:

package sf;

import java.util.LinkedList;

import java.util.Scanner;

/*

你可能感兴趣的:(分支限界法,01背包,java)