01背包问题的多种解法

问题描述:
有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出重量不超过W的物品,求所有挑选方案中价值总和的最大值。对于每一个物品都只有选和不选的两种选择;所以称为01背包问题。

递归解法:
  首先说明记忆型递归(也叫记录型递归)的特点,记忆型递归主要用于求解子问题有重叠现象的递归;是为了避免将一个子问题多次求解;所以在求解子问题是将子问题的解记录下来,到再一次碰到相同的子问题时不需要再一次求解,直接查询记录的结果,这样可以提高效率。到子问题重叠的次数越多效率提高越快。
  再看01背包问题,可以发现它和求集合的子集问题很相似;对于每一个物品都面临选和不选的问题。也就是说每一次选择物品时都会产生两条分支。所以我们可以先用DFS的方法求解01背包问题,然后再DFS的基础上将其改变为记忆型递归。

普通递归代码如下:


public class 记忆型递归解决01背包问题 {
	static int[] w = {2,1,3,2};//重量表
	static int[] v = {3,2,4,1};//价值表
	static int n = 4;//物品数量
	static int  W = 5;//背包的承重极限
	public static void main(String[] args){
		System.out.println(dfs(0, W));
	}
	private static int dfs(int i, int ww) {
		if(i==n) return 0;//没有物品选择了
		if(ww<=0) return 0;//容量不足了
		int v1 =  dfs(i+1, ww);//不选当前物品,容量不变
		if(ww>=w[i]){
			int v2 = v[i] + dfs(i+1, ww-w[i]);//选择当前物品
			return Math.max(v1, v2);
		}
		return v1;
	}
}

记忆型递归代码如下:


import java.util.Arrays;

public class 记忆型递归解决01背包问题 {
	static int[] w = {2,1,3,2};//重量表
	static int[] v = {3,2,4,1};//价值表
	static int n = 4;//物品数量
	static int  W = 5;//背包的承重极限
	static int[][] rec = new int[n][W+1];//记录表
	public static void main(String[] args){
		//Arrays.fill(rec, -1);//将记录表初始化为-1
		for(int i=0; i<n; i++){
			for(int j=0; j<W+1; j++){
				rec[i][j] = -1;
			}
		}
		System.out.println(recdfs(0, W));
	}
	
	/*
	 * i表示从第几个物品开始选,ww表示剩余容量
	 */
	private static int recdfs(int i, int ww) {
		if(i==n) return 0;//没有物品选择了
		if(ww<=0) return 0;//容量不足了
		if(rec[i][ww]>=0){//计算前先检查是否有记录
			return rec[i][ww];
		}
		int v1 =  recdfs(i+1, ww);//不选当前物品,容量不变
		if(ww>=w[i]){
			int v2 = v[i] + recdfs(i+1, ww-w[i]);//选择当前物品
			rec[i][ww] = Math.max(v1, v2);//返回前,先记录子问题的解
			return Math.max(v1, v2);
		}
		rec[i][ww] = v1;//返回前,先记录子问题的解
		return v1;
	}
}


动态规划解法:
  动态规划解法就是建立dp表,行表示可选范围,比如第三行表示可选范围是前三个物品;列表示剩余的容量。首先初始化第一行,之后的每一行都可以根据之前的记录得出结果。

代码如下:


import java.util.Arrays;

public class 动态规划解决01背包问题 {
	static int[] w = {2,1,3,2};//重量表
	static int[] v = {3,2,4,1};//价值表
	static int n = 4;//物品数量
	static int  W = 5;//背包的承重极限
	static int[][] rec = new int[n][W+1];//记录表
	public static void main(String[] args){
		for(int i=0; i<n; i++){
			Arrays.fill(rec[i], -1);//将记录表初始化为-1
		}
		System.out.println(dp());
	}
	
	private static int dp() {
		int[][] dp = new int[n][W+1];
		
		//初始化第一行
		for(int j=0; j<W+1; j++){
			if(j>=w[0]){//剩余重量大于第一个物品的重量
				dp[0][j] = v[0];
			}else{
				dp[0][j] = 0;
			}
		}
		
		//开始构建dp表
		for(int i=1; i<n; i++){
			for(int j=0; j<W+1; j++){
				if(j>=w[i]){//要得起,剩余容量大于第i个物品的重量
					int v1 = v[i] + dp[i-1][j-w[i]];//要
					int v2 = dp[i-1][j];//不要
					dp[i][j] = Math.max(v1, v2);
				}else{//要不起
					dp[i][j] = dp[i-1][j];
				}
			}
		}
		return dp[n-1][W];
	}

}

猜你喜欢

转载自blog.csdn.net/HC199854/article/details/104835605
今日推荐