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号物品)
2. ①对于零一背包求解最大价值的问题,我们是可以使用很多方法来解决的,比如经典的解法有动态规划,而且动态规划求解的时间复杂度比较低,使用推导的方法求解出其中的dp公式或者状态转移方程,第二种是使用记忆性的递归来求解,使用的是对普通的递归改进之后的递归调用并且使用额外的空间记录下其中的中间过程来进行求解的,时间复杂度也比较低,第三种是使用普通的递归和深度优先搜索来进行解决,对于一开始接触深搜算法的学习者来说多写写这种代码对于这种算法的理解是很有帮助的,下面使用深度优先搜索来解决
② 对于背包中的当前物品,我们是可以选择,也可以不选,所以对于当前的物品选择只有两种情况,这也是区别于完全背包问题一个点,完全背包问题是可以重复拿取物品的,分析到这里是不是想到了部分和的那到题目,两道题目的思路几乎是一样的,对于当前的数字或者物品,我们可以选择要,也可以选择不要,所以可以知道这道题目存在着两个平行的状态,这样我们就解决了深搜的第一步:通过分析找到存在的平行状态有多少个(决定着我们调用多少个的dfs)
③ 确定好平行状态的个数之后,画出搜索树,来确定对于平行状态下退回来的状态该如何进行处理才能够解决我们的问题,下面是搜索树:(例子: (2,3),(1,2),(3,4),(2,2))
(0表示不选择当前的元素)
④ 观察搜索树来确定在每一条路径走完之后该如何对当前的数据进行处理,可以观察发现当dfs被if条件进行剪枝之后发现不能够搜索下去之后那么就退出当前的路径的往下搜索了,所以这是第一种走完一条路径的情况,第二种是没有物品可以选择了,此时此时这条路径说明也走完了,所以在这个时候每当走完一条路径我们都应该与当前的max进行比较来决定是否更新最大值
可以观察发现我们可以在递归的出口和第一个平行状态调用完成之后就与当前的max进行比较来决定是否更新最大值(可以从搜索树看出),因为这两种情况都是对应着走完路径的情况
⑤ 因为涉及到不可以重复拿取的限制,在dfs的方法中应该设置一个局部变量来记录当前调用到哪一层了来确定是否拿取当前的物品,在完全背包的问题的深搜中是不用设置局部变量来记录调用到哪一层了,因为它是可以重复拿取的,而且调用的层数往往是不确定,所以不能够设置这个局部变量来作为递归的出口,设置了反而会出现错误,所以设置局部变量来记录也没有什么用处
⑥ 为了尽量减少在方法中传递参数我们可以将不变的量设置为全局变量,这样也可以方便我们进行操作,这里特别要注意局部变量的参数最好不要跟全局变量的参数一样
⑦ 对第一个平行状态的if语句充当剪枝和间接出口的作用,一开始的递归的出口是为了在第二个平行状态在没有物品可以拿取的时候可以退出搜索所以需要设置这个出口,所以递归的出口假如没有的话会导致第二个平行状态往下调用的时候导致数组越界的问题
3. 具体的代码如下:
import java.util.Scanner;
public class Main{
static int n;
static int w[];
static int v[];
static int W;
static int max = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
w = new int[n];
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();
}
W = sc.nextInt();
dfs(0, 0, W);
System.out.println(max);
sc.close();
}
private static void dfs(int cur, int value, int W){
if(cur == n){
if(max < value ){
max = value;
}
return;
}
if(w[cur] <= W){
dfs(cur + 1, value + v[cur], W - w[cur]);
if(max < value + v[cur]){
max = value + v[cur];
}
}
//下面的dfs搜索的平行状态是不能够加上if条件的
dfs(cur + 1, value, W);
}
}
测试数据:
6
1 3 2 1 2 4
3 1 2 5 4 1
6
输出为14