zoukankan      html  css  js  c++  java
  • 回溯算法_01背包问题_Java实现

    原文地址:http://blog.csdn.net/ljmingcom304/article/details/50314839
    本文出自:【梁敬明的博客】

    1.回溯算法  

      回溯算法也叫试探法,通俗的将就是一个方向的路一直往前走,能走则走,不能走则退回来换一个方向再试。一般的实现步骤是:针对一个问题定义解的空间,至少包含问题的一个最优解;用易于搜索的解空间结构,使得能用回溯方法搜索整个解空间;以深度优先的方式搜索整个解空间,并在搜索过程中通过剪枝函数避免无效搜索。
    回溯算法
       这里写图片描述
      如上图所示,每个节点均有左右两种选择,最终要实现结果会产生AH、AI、AJ、AK、AL、AM、AN和AO八种实现方案,需要确定哪种方案为最优方案。以左边的条件为首要条件进行判断,那么其搜索路线为:【A】→【B】→【D】→【H】→【I】→【E】→【J】→【K】→【C】→【F】→【L】→【M】→【G】→【N】→【O】,即在全部满足限制条件的前提下,实现每个节点之间的两两比较,最终选择出最优方案。

    2.背包问题  

      一个旅行者有一个最多能装m公斤的背包,现在有n中物品,每件的重量分别是W1、W2、……、Wn,每件物品的价值分别为C1、C2、……、Cn, 需要将物品放入背包中,要怎么样放才能保证背包中物品的总价值最大?

    3.算法分析 

      上述问题包含两种情况,一种是将物品放入背包中,另一种是不将物品放入背包中。其中隐含着三个限制条件。第一个限制条件为放入物品的总和不能超过背包的总承重,即当放入的物品总和超过背包的总承重时,即使背包中物品的总价值再大也毫无意义。第二个限制条件为要保证背包中物品的总价值最大。无论第一个条件成不成立,第二个条件总是在其之上的一种更优的选择。第三个限制条件为当达到物品的数量上限,即没有可以获取物品再放入背包时,前两个限制条件均毫无意义,因此需要优先进行判断。
      这里我们可以将第一种情况看做左节点,第二种情况看做右节点,第三个限制条件用于终结节点的搜索。因此在解决该问题时,首先需要判断是否有物品可以放到背包中,要是没有物品可以放到背包中,直接返回结果,当前的价值即为背包的最优价值。然后判断左节点是否为可行节点,当左节点可行,就优先搜索左子树,不满足则进入右子树。当达到物品的数量上限,直接返回当前物品数量的结果;若左右节点同时不可行,则直接将结果返回上一步。以此类推,将结果从下至上一层一层进行返回,得到问题的最优结果。
      创建一个物品对象,分别存在价值、重量以及单位重量价值三种属性。

    public class Knapsack implements Comparable<Knapsack> {
        /** 物品重量 */
        private int weight;
        /** 物品价值 */
        private int value;
        /** 单位重量价值 */
        private int unitValue;
    
        public Knapsack(int weight, int value) {
            this.weight = weight;
            this.value = value;
            this.unitValue = (weight == 0) ? 0 : value / weight;
        }
    
        public int getWeight() {
            return weight;
        }
    
        public void setWeight(int weight) {
            this.weight = weight;
        }
    
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
    
        public int getUnitValue() {
            return unitValue;
        }
    
        @Override
        public int compareTo(Knapsack snapsack) {
            int value = snapsack.unitValue;
            if (unitValue > value)
                return 1;
            if (unitValue < value)
                return -1;
            return 0;
        }
    
    }

    按照回溯算法将物品放入背包中。

    public class HSSFProblem {
    
        // 待选择的物品
        private Knapsack[] bags;
        // 背包的总承重
        private int totalWeight;
        // 背包的当前承重
        private int currWeight;
        // 待选择物品数量
        private int n;
        // 放入物品后背包的最优价值
        private int bestValue;
        // 放入物品和背包的当前价值
        private int currValue;
    
        public HSSFProblem(Knapsack[] bags, int totalWeight) {
            this.bags = bags;
            this.totalWeight = totalWeight;
            this.n = bags.length;
    
            // 物品依据单位重量价值从大到小进行排序
            Arrays.sort(bags, Collections.reverseOrder());
        }
    
        public int solve(int i) {
    
            // 当没有物品可以放入背包时,当前价值为最优价值
            if (i >= n) {
                bestValue = currValue;
                return bestValue;
            }
    
            // 首要条件:放入当前物品,判断物品放入背包后是否小于背包的总承重
            if (currWeight + bags[i].getWeight() <= totalWeight) {
                // 将物品放入背包中的状态
                currWeight += bags[i].getWeight();
                currValue += bags[i].getValue();
    
                // 选择下一个物品进行判断
                bestValue = solve(i + 1);
    
                // 将物品从背包中取出的状态
                currWeight -= bags[i].getWeight();
                currValue -= bags[i].getValue();
            }
    
            // 次要条件:不放入当前物品,放入下一个物品可能会产生更优的价值,则对下一个物品进行判断
            // 当前价值+剩余价值<=最优价值,不需考虑右子树情况,由于最优价值的结果是由小往上逐层返回,
            // 为了防止错误的将单位重量价值大的物品错误的剔除,需要将物品按照单位重量价值从大到小进行排序
            if (currValue + getSurplusValue(i + 1) > bestValue) {
                // 选择下一个物品进行判断
                bestValue = solve(i + 1);
            }
            return bestValue;
        }
    
        // 获得物品的剩余总价值
        public int getSurplusValue(int i) {
            int surplusValue = 0;
            for (int j = i; j < n; j++)
                surplusValue += bags[i].getValue();
            return surplusValue;
        }
    
    }

    最终测试结果:90

    public class HSSFTest {
    public static void main(String[] args) {
        Knapsack[] bags = new Knapsack[] { new Knapsack(2, 13),
                new Knapsack(1, 10), new Knapsack(3, 24), new Knapsack(2, 15),
                new Knapsack(4, 28), new Knapsack(5, 33), new Knapsack(3, 20),
                new Knapsack(1, 8) };
        int totalWeight = 12;
        HSSFProblem problem = new HSSFProblem(bags, totalWeight);
    
        System.out.println(problem.solve(0));
    }
    }

    欢迎关注我的微信公众号:“”Java面试通关手册(坚持原创,分享美文,分享各种Java学习资源,面试题,以及企业级Java实战项目回复关键字免费领取):
    微信公众号

  • 相关阅读:
    浅谈Lyndon分解
    【CF914G】Sum the Fibonacci(FWT)
    【洛谷6914】[ICPC2015 WF] Tours(非树边随机边权,树边边权异或)
    【洛谷7143】[THUPC2021 初赛] 线段树(动态规划)
    【洛谷7325】[WC2021] 斐波那契(数论)
    【CF666E】Forensic Examination(广义后缀自动机+线段树合并)
    【CF685C】Optimal Point(二分答案)
    【洛谷7364】有标号二分图计数(多项式开根)
    【CF679E】Bear and Bad Powers of 42(ODT+线段树)
    【洛谷5307】[COCI2019] Mobitel(动态规划)
  • 原文地址:https://www.cnblogs.com/snailclimb/p/9086364.html
Copyright © 2011-2022 走看看