01背包问题的多种解法

Posted 覃会程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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];
	



以上是关于01背包问题的多种解法的主要内容,如果未能解决你的问题,请参考以下文章

ACM:动态规划,01背包问题

多重背包问题

由DAG到背包问题——记忆化搜索和递推两种解法

0-1背包问题 —— 四种解法解题

背包问题

c语言背包问题,求高手解答