0/1背包问题的通用模板

以lintcode 92为例。
整体架构如下:
0/1-knapsack
代码:

import java.util.Arrays;

public class Solution {
	
	/**
	 * 是否可以全部选择
	 */
	private int fullSelect(int[] A,int m){
		int sum=0;
		for(int key:A){
			sum+=key;
		}
		return sum;
	}
	
	/**
	 * 贪心选择
	 */
	private int greedy(int[] A,int m){
		Arrays.sort(A);
		int sum=0;
		for(int i=A.length-1;i>=0;i--){
			if(sum+A[i]<m){
				sum+=A[i];
			}
		}
		return sum;
	}
	
	/**
	 * 全局变量,进行位运算
	 */
	private int totalKey=0;
	/**
	 * 全局变量,用来记录最优解
	 */
	private int totalMax=0;
	private int resultKey=0;
	/**
	 * @param level 当前递归层数
	 * @param curr  当前重量
	 * @param res   剩余重量
	 */
	private void dfs(final int[] A,final int m,final int n,int level,int curr,int res){
		if(level==n){
			//更新最优值
			if(curr>=totalMax){
				totalMax=curr;
				resultKey=totalKey;
			}
		}else{
			res-=A[level];
			//不选择
			if(curr<=m&&curr+res>=totalMax){
				dfs(A, m, n, level+1, curr, res);
			}
			//第level位置1,选择
			totalKey|=1<<level;
			curr+=A[level];
			//约束和剪枝
			if(curr<=m&&curr+res>=totalMax){
				dfs(A, m, n, level+1, curr, res);
			}
			//回溯,第level位置重新置为0
			totalKey&=~(1<<level);
		}
	}
	
	/**
	 * 通过位运算还完最优解
	 */
	private int getResultBitManipulation(int[] A,int n){
		int sum=0;
		for(int i=0;i<n;i++){
			//判断第i位是0还是1
			if((resultKey&1<<i)!=0){
				sum+=A[i];
			}
		}
		return sum;
	}
	
	/**
	 * 回溯剪枝解法
	 */
	public int backTrackSolution(int[] A,int m){
		int n=A.length;
		//使用贪心算法确定一个下界
		this.totalMax=greedy(A, m);
		int res=0;
		for(int key:A){
			res+=key;
		}
		dfs(A, m, n, 0, 0, res);
		return getResultBitManipulation(A, n);
	}
	
	public int dpSolution(int[] A,int m){
		int n=A.length;
		int[][] dp=new int[n+1][m+1];
		//计算最优值
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				//不放入i
				dp[i][j]=dp[i-1][j];
				if(j>=A[i-1]){
					//放入i,并把前i-1个物品放入容量为j-A[i]的背包
					dp[i][j]=Math.max(dp[i][j], dp[i-1][j-A[i-1]]+A[i-1]);
				}
			}
		}
		//根据最优值得到的信息,反向构造最优解
		int c=m;
		int[] vec=new int[n];
		for(int i=n;i>0;i--){
			if(dp[i][c]==dp[i-1][c]){
				vec[i-1]=0;
			}else{
				vec[i-1]=1;
				c-=A[i-1];
			}
		}
		int sum=0;
		for(int i=0;i<n;i++){
			if(vec[i]==1){
				sum+=A[i];
			}
		}
		return sum;
	}
    public int backPack(int m, int[] A) {
    	//边界检查
    	if(m==0||A==null||A.length==0){
    		return 0;
    	}
        int full=fullSelect(A, m);
        if(full<=m){
        	return full;
        }
        if(m<=16){
        	return backTrackSolution(A, m);
        }else{
        	return dpSolution(A, m);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值