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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM