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