(回溯剪枝)0/1背包详解

基本思路:要选择的物品有两种状态,选或者不选,很明显这是一个子集问题,所以递归部分参照子集模板修改,选了物品i就i+1,tw+w[i],tv+v[i]到下一层,不选就不变光i+1到下一层,然后每个子集(叶子节点)都比较得出的tv(价值),最后得出最优解(最大价值)。 

本文测试样例:

5 10
2 6
2 3
6 5
5 4
4 6

输出样例:

15

(回溯剪枝)0/1背包详解_第1张图片

(回溯剪枝)0/1背包详解_第2张图片

子集树:

(回溯剪枝)0/1背包详解_第3张图片

递归(回溯)代码(含左孩子剪枝,易懂):

import java.util.*;
public class _01背包 {
	static int n,c;//物品数量和背包容量
	static int[] w,v;//物品重量和物品价值
    static int x[];//存放最优解
    static int[] op;//存放临时解
    static int maxv;//存放最优解的总价值
  //i是数组起始位置,tw表示装入背包中的物品总重量,tv表示背包中物品的总价值
    static void dfs(int i,int tw,int tv,int[] op){
    	if(i>n){                //找到一个叶子节点
    		if(tw<=c&&tv>maxv){ //找到一个满足条件的更优解,保存它
    			maxv=tv;
    			for(int j=1;j<=n;j++){
    				x[j]=op[j];
    			}
    		}
    	}
    	else{                //尚未找完所有物品
    		if(tw+w[i]<=c){   //左孩子节点剪枝:满足条件才放入第i个物品
    			op[i]=1;         //选取第i个物品
        		dfs(i+1,tw+w[i],tv+v[i],op);
    		}
    		op[i]=0;         //不选取第i个物品 ,回溯
    		dfs(i+1,tw,tv,op);
    	}
    }
    
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Scanner in=new Scanner(System.in);
        n=in.nextInt();
        c=in.nextInt();
        w=new int[n+1];
        v=new int[n+1];
        x=new int[n+1];
        op=new int[n+1];
        for(int i=1;i<=n;i++){
        	w[i]=in.nextInt();
        	v[i]=in.nextInt();
        }
        dfs(1,0,0,op);
        System.out.println("最佳放入方案:");
        for(int i=1;i<=n;i++){
        	if(x[i]==1){
        		System.out.println(w[i]+" "+v[i]);
        	}
        }
        System.out.println("最优值为:"+maxv);
	}

}

 

剪枝思路:

 回溯剪枝代码: (该右孩子简剪枝方法适用于求装入背包的重量等于限制重量!

import java.util.*;
public class _01背包_剪枝 {
	static int n,c;//物品数量和背包容量
	static int[] w,v;//物品重量和物品价值
    static int x[];//存放最优解
    static int[] op;//存放临时解
    static int maxv;//存放最优解的总价值
  //i是数组起始位置,tw表示装入背包中的物品总重量,tv表示背包中物品的总价值
    static void dfs(int i,int tw,int rw,int tv,int[] op){
    	if(i>n){                //找到一个叶子节点
    		if(tw<=c&&tv>maxv){ //找到一个满足条件的更优解,保存它
    			maxv=tv;
    			for(int j=1;j<=n;j++){
    				x[j]=op[j];
    			}
    		}
    	}
    	else{                //尚未找完所有物品
    		if(tw+w[i]<=c){   //左孩子节点剪枝:满足条件才放入第i个物品
    			op[i]=1;         //选取第i个物品
        		dfs(i+1,tw+w[i],rw-w[i],tv+v[i],op);
    		}
    		if(tw+rw>=c){     //右孩子节点剪枝
    			op[i]=0;         //不选取第i个物品 ,回溯
        		dfs(i+1,tw,rw-w[i],tv,op);
    		}
    	}
    }
    
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Scanner in=new Scanner(System.in);
        n=in.nextInt();
        c=in.nextInt();
        w=new int[n+1];
        v=new int[n+1];
        x=new int[n+1];
        op=new int[n+1];
        int sum=0;
        for(int i=1;i<=n;i++){
        	w[i]=in.nextInt();
        	v[i]=in.nextInt();
        	sum+=w[i];
        }
        dfs(1,0,sum,0,op);
        System.out.println("最佳放入方案:");
        for(int i=1;i<=n;i++){
        	if(x[i]==1){
        		System.out.println(w[i]+" "+v[i]);
        	}
        }
        System.out.println("最优解为:"+maxv);
	}

}

 

 

 

你可能感兴趣的:(算法设计与分析课)