zoukankan      html  css  js  c++  java
  • 01背包问题_回溯法&分支限界法

    package 分支限界法;
    
    import java.util.LinkedList;
    import java.util.Scanner;
    
    /*01背包问题*/
    public class ZOPackage {
        /*
         * 主方法
         */
        public static void main(String[] args) {
            //输入数据
            System.out.println("请输入背包的容量w和物品的个数n");
            Scanner in = new Scanner(System.in);
            int w = in.nextInt();// 背包的容量
            int n = in.nextInt();// 物品的个数
            int solution=-1;
            BLBag[] p = new BLBag[n];
            System.out.println("请依次输入各个物品的名称s和重量w和价值v");
            int value;
            int weigth;
            String pid;
            for (int i = 0; i < n; i++) {
                pid = in.next();
                weigth = in.nextInt();
                value = in.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<n;i++) a[i]=null;
            //对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<p.length;k++)//循环名字
            {
                //贪心算法求解问题(修改版)
                if(p[k].weight<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<Node> nodelist=new LinkedList<Node>();
            LinkedList<Node> nodesolution=new LinkedList<Node>();
            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()<p.length)
                {              
                    //左节点
                    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<Node> 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<p.length;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;
        }
    }
    /******
    运行结果
    请输入背包的容量w和物品的个数n
    10 5
    请依次输入各个物品的名称s和重量w和价值v
    p1 2 6
    p2 2 3
    p3 6 5
    p4 5 4
    p5 4 6
    放入了物品:p1
    放入了物品:p2
    放入了物品:p5
    最优解为:15
    *********/
    package 回溯法;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Scanner;
    
    public class ZOPakage {
        public static void main(String[] args) {
            System.out.println("请输入背包的容量w和物品的个数n");
            Scanner in = new Scanner(System.in);
            int w = in.nextInt();// 背包的容量
            int n = in.nextInt();// 物品的个数
            BLBag[] bags = new BLBag[n];
            System.out.println("请依次输入各个物品的名称s和重量w和价值v");
            int value;
            int weigth;
            String pid;
            for (int i = 0; i < n; i++) {
                pid = in.next();
                weigth = in.nextInt();
                value = in.nextInt();
                bags[i] = new BLBag(pid, weigth, value);
            }
            HSSFProblem problem = new HSSFProblem(bags, w);
            System.out.println("最优解为:"+problem.solve(0));
        }
    }
    
    class BLBag implements Comparable<BLBag> {
        /** 物品名字*/
        private String name;
        /** 物品重量 */
        private int weight;
        /** 物品价值 */
        private int value;
        /** 单位重量价值 */
        private int unitValue;
    
        public BLBag(String name,int weight, int value) {
            this.weight = weight;
            this.value = value;
            this.name = name;
            this.unitValue = (weight == 0) ? 0 : value / weight;
        }
    
        public String getname() {
            return name;
        }
    
        public void setname(int weight) {
            this.name = name;
        }
        
        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(BLBag snapsack) {
            int value = snapsack.unitValue;
            if (unitValue > value)
                return 1;
            if (unitValue < value)
                return -1;
            return 0;
        }
    }
    
    class HSSFProblem {
    
        // 待选择的物品
        private BLBag[] bags;
        // 背包的总承重
        private int totalWeight;
        // 背包的当前承重
        private int currWeight;
        // 待选择物品数量
        private int n;
        // 放入物品后背包的最优价值
        private int bestValue;
        // 放入物品和背包的当前价值
        private int currValue;
    
        public HSSFProblem(BLBag[] 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;
        }
    
    }
    /**
    运行结果
    请输入背包的容量w和物品的个数n
    10 5
    请依次输入各个物品的名称s和重量w和价值v
    p1 2 6
    p2 2 3
    p3 6 5
    p4 5 4
    p5 4 6
    最优解为:15
    **/

    分支限界详解:https://www.cnblogs.com/RB26DETT/p/10982687.html#top

    动态规划、分支限界、回溯对比:https://www.jianshu.com/p/270acca3e6fa

  • 相关阅读:
    Spring 总结
    分布式缓存Memcache
    Docker原理 -- namespace与CGroup
    JDK源码分析--Collections
    React生命周期总结
    【转】前端优化的35条
    http缓存与cdn相关技术
    跨域处理
    sequekize
    orm2
  • 原文地址:https://www.cnblogs.com/LieYanAnYing/p/12051091.html
Copyright © 2011-2022 走看看