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

猜你喜欢

转载自www.cnblogs.com/LieYanAnYing/p/12051091.html