1. 问题描述:
有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
1≤n≤100
1≤wi,vi≤100
1≤W≤10000
输入:
n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
W=5
输出:
7(选择第0,1,3号物品)
(因为对每个物品只有选和不选两种情况,所以这个问题称为01背包)
2. 思路分析:
01背包问题与部分背包问题很类似,但是部分背包问题是物品是可以进行分割的,假如不能够拿取当前的物品,那么可以拿取这个物品的某一部分来构成最大质量并且价值是最大的
但是01背包问题中物品是不可以进行分割的,可以选择拿取当前的物品也可以选择不拿这两种情况,所以这个是两个问题的区别
我们先不考虑怎么样写计算机程序,先是看自己的直觉思维,怎么样拿取物品才能够使拿取的物品最终的价值最大但是不超过最大质量
首先我们可能考虑先凑成目标的最大质量,W = 5 那么我们可以由多个组合来拿取,可以拿取0,1, 3号物品,0,2号物品,也可以拿取2,3号物品,再在这几个组合中找出价值最大的,可以发现0,1, 3号物品和2,3号物品的价值是最大的
但是除了可以凑成目标最大质量情况有可能价值最大,也可能是这样的情况:不能够凑成最大质量,但是它的总价值有可能是最大的
我们可以拿取当前的物品,也可以不拿取,这种思维模式是不是很像求解非空集合的那到题目呢?的确两道题目都可以这样来想,拿或者不拿这两种情况,只不过非空子集那道题目要记录中间的过程,这道题目只是求解出最大价值即可
所以,我们可以使用递归的方式来进行解决,在所有的可能中找出一个能够不超过目标质量但是价值是最大的,找出所有可能这实际上也是一种深度优先搜索的过程
我们在遇到问题的时候要先进行给出测试样例的分析,利用简单的推导,类比,找出问题的相似性来进行问题的求解
下面是使用普通递归的方法来进行解决(也可以叫做深搜来解决:实际上就是深搜)
其中递归有两个变量在进行变化一个是可以选择的当前物品的下标,一个是可以选择的当前物品的最大质量,所以在方法中传入两个参数来,还有其他的不变的参数也可以传递进来方便操作,也可以将这些设置为全局变量不用传递进来都可以
3. 代码如下:
import java.util.Scanner;
import static java.lang.Math.max;;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int w[] = new int[n];
int v[] = new int[n];
for(int i = 0; i < n; i++){
w[i] = sc.nextInt();
}
for(int i = 0; i < n; i++){
v[i] = sc.nextInt();
}
int W = sc.nextInt();
//使用普通的递归来解决,这里的方法名也写作dfs
int res = dfs(w, v, 0, n, W);
System.out.println(res);
sc.close();
}
private static int dfs(int[] w, int[] v, int cur, int n, int W){
if(cur == n) return 0; //没有东西可以拿了
//可以不使用if语句来进行限制因为后面的时候已经使用了if语句来进行剪枝当不能够拿取物品的时候不会再继续搜索下去
//if(W < 0) return 0; //不可以装东西了
int v1 = dfs(w, v, cur + 1, n, W);
//剪枝(看能不拿当前的物品能够拿才搜索下去)
if(W >= w[cur]){
int v2 = v[cur] + dfs(w, v, cur + 1, n, W - w[cur]);
return max(v1, v2);
}else{
//当遇到比较大的质量的物品导致不能够装的时候直接返回当前的v1
return v1;
}
}
}
测试用例:
5
1 2 3 4 5
5 4 3 2 1
10
输出14