背包问题:递归实现和动态规划实现

问题描述

给定 n 件物品,物品的重量为 w[i],物品的价值为 v[i]。现挑选物品放入背包中,假定背包能承受的最大重量为 W,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

今天上课的时候讲到,对于一个01背包,每个物品都可以放或者不放两种状态,突然想到可以使用递归的方法解决这个题,于是有了这篇文章。一般方法为动态规划,在后面进行讲解。

递归实现

思路分析

先上一张图
在这里插入图片描述

为什么一个背包问题可以画一个树的图?
别急,让我慢慢道来:

图中每个节点代表一种状态,每条边代表一种操作
每个节点连接左子树的边为1,连接右子树的边为0:
1代表将该物品放入背包
0代表不将该物品放入背包
两棵左孩子其实就是对同一元素进行不同的操作后得到的新状态

树有一个特点就是:树的孩子还是一棵树。那么这就为我们用递归实现该问题提供了可能.

(1)没有出现叶子结点的情况时( weight 数组和 value 数组最后一个元素还未进行是否放入背包的操作),递归调用该函数,对下一个元素进行操作
(2)当遇到叶子结点(即 weight 数组和 value 数组走到最后的时候),遇到递归出口,开始退栈

代码实现

public class Bag {
    
    
    public static void main(String[] args) {
    
    
        int[] weight = new int[]{
    
    16,15,15}; //重量
        int[] value = new int[]{
    
    45,25,25};  //价值
        int capacity = 30;                  //背包容量
        int max1 = maxValue(weight, value, 0, capacity, 0, true);
        int max2 = maxValue(weight, value, 0, capacity, 0, false);
        System.out.println(Math.max(max1, max2));
    }

    public static int maxValue(int[] weight, int[] value, int i, int surplusCapacity,int nowValue, boolean isPutIn){
    
    
        if(i >= weight.length){
    
         //递归出口,越界时返回当前价值
            return nowValue;
        }
        //剩余容量大于当前物品的重量
        if( isPutIn && surplusCapacity >= weight[i]){
    
    
            nowValue += value[i];           //更新当前价值
            surplusCapacity -= weight[i];   //更新当前容量
        }
        int max1, max2;
        /*假如可以放入,假如的原因是不一定能放入*/
        max1 = maxValue(weight, value, i+1, surplusCapacity, nowValue, true);
        /*不想放入*/
        max2 = maxValue(weight, value, i+1, surplusCapacity, nowValue, false);
        return Math.max(max1,max2);
    }
}

动态规划实现

思路分析

我们先来考虑一个由前 i 个物品(1 ≤ i ≤ n)定义的实例,物品的重量分别为 w 1 , . . . w i w_1,...w_i w1,...wi,价值分别为 v 1 , . . . v i v_1,...v_i v1,...vi,背包的重量为 j (1 ≤ j ≤ W)。设dp(i, j)为该实例的最优解的物品总价值,也就是说,是能够放进承重量为 j 的背包中的前 i 个物品中最有价值子集的总价值。可以把前 i 个物品中能够放进承重量为 j 的背包中的子集分成两类; 包括第 i 个物品的子集和不包括第 i 个物品的子集

(1)在不包括第 i 个物品的子集中, 最优子集的价值是dp(i - 1,j)
(2)在包括第 i 个物品的子集中(此时必须满足: j − w i j - w_i jwi ≥ 0),最优子集是由该物品的前i - 1个物品中能够放进承重量为 j − w i j - w_i jwi的背包的最优子集组成。这种最优子集的总价值为 v i + d p ( i − 1 , j − w i ) v_i + dp(i-1,j-w_i) vi+dp(i1,jwi)

状态转移方程如下:
d p ( i , j ) = { m a x { d p ( i − 1 , j ) , v i + d p ( i − 1 , j − w i ) } , j − w i ≥ 0 d p ( i − 1 , j ) , j − w i < 0 dp(i ,j) = \begin{cases} max\{dp(i-1,j),v_i + dp(i-1,j-w_i)\},& \text{$j-w_i≥0$}\\ dp(i-1,j),& \text{$j-w_i < 0$} \end{cases} dp(i,j)={ max{ dp(i1,j),vi+dp(i1,jwi)},dp(i1,j),jwi0jwi<0

代码实现

import static java.lang.Integer.max;

public class Bag {
    
    
    public static void main(String[] args) {
    
    
        int result = maxValue(new int[]{
    
    16,15,15},new int[]{
    
    45,25,25},30);

        System.out.println(result);
    }
    public static int maxValue(int[] weight, int[] value,int capacity){
    
    
        int n = weight.length;         //物品个数
        int [][]dp = new int[n+1][capacity+1];
        dp[0][0] = 0;
        for(int i = 1; i < n+1; i++){
    
    
            for(int j = 1; j < capacity+1; j++){
    
    
                if( j - weight[i-1] >= 0 ){
    
     //可以放得下当前物品
                    dp[i][j] = max(dp[i-1][j],value[i-1]+dp[i-1][j-weight[i-1]]);
                }else{
    
    
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n][capacity];
    }
}

关于背包问题的动态规划还有很多的优化处理,我这里只记录下最基本的一种。
下面的背包九讲,对背包问题有非常详细的讲解
背包九讲
欢迎下载

猜你喜欢

转载自blog.csdn.net/BWQ2019/article/details/110563653