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
