动态规划DP——01背包问题

01 背包问题

  今天在算法课上讲解了动态规划算法,其中讲到了01背包问题。这是一种典型的动态规划问题,于是下课之后我使用java进行了相对应的代码实现。动态规划求解具有以下的性质:
1.最优子结构性质:最优解包含了其子问题的最优解,不是合并所有子问题的解,而是找最优的一条解线路,选择部分子最优解来达到最终的最优解。
2.子问题重叠性质:先计算子问题的解,再由子问题的解去构造问题的解(由于子问题存在重叠,把子问题解记录下来为下一步使用,这样就直接可以从备忘录中读取)。其中备忘录中先记录初始状态。

题目描述

现有一个容量大小为V的背包和N件物品,每件物品有两个属性,体积和价值,请问这个背包最多能装价值为多少的物品?

输入描述
第一行两个整数V和n。
接下来n行,每行两个整数体积和价值。1≤N≤1000,1≤V≤20000。
每件物品的体积和价值范围在[1,500]。
输出描述
输出背包最多能装的物品价值。
输入
6 3
3 5
2 4
4 2
输出
9

  首先我们来分析这个问题,它不同于背包问题的是一个物品只有放入背包和不放入背包两种状态,所以我们将这两种状态可以称之为01。这种问题一般是不能使用贪心算法进行解决的,我们通常使用动态规划的方法进行求解。因为这类问题具有最优子结构性质,也就是说整个问题的最优解一定包含了子问题的最优解,于是乎我们就可以在求解子问题的同时将解进行记录(备忘录的思想)从而降低整个问题求解的时间复杂度。
  依据输入样例我们可以对该问题进行分析,背包的容量为6,物品总共有3个。我们不妨记函数 V(i,j) 代表整个背包存放物品的价值,其中 i 表示第几个物品,j 表示背包的体积。记volume(i) 为第i个物品的体积,记value(i) 为第i个物品的价值。我们可以得到状态转移方程:
当volume(i) > j 时: V(i,j) = V(i-1,j) 此时背包放不下该物品
当volume(i) <= j 时: V(i,j) = max{V(i-1,j) , V(i-1,j-volume(i))+value(i)}
此时背包容量足够,在不放该物品 & 放该物品并不放上一个物品当中取最大值。
  我们可以用表格的形式来具体描述这个二维数组, 首先我们需要将数据初始化,也就是将第一行第一列赋值为0,因为无论是背包容量为0,还是物品为第0个(我们约定 i 从 1 开始) 在这里插入图片描述
当j=3 也就是背包容量为3时 volume(1)=j v(i,j)=max{0,5}
在这里插入图片描述
当i=2 j=5此时物品2 volume(1) < j v(i,j)=max{v(1,5),v(1,3)+4}
在这里插入图片描述
进一步完成表格
在这里插入图片描述
于是这个01背包问题也就转变成了一个二维数组的填数字问题,我们只需要按照状态方程去判断进行计算填满表格,就能够得到所背包的最大价值。

实现代码
import java.util.*;

public class Main {
public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int v = sc.nextInt();//背包容量
    int n = sc.nextInt();//商品的个数
    Map<Integer,Map<Integer,Integer>> map = new HashMap<>();//容器用于存储商品键值对
    for(int i=0; i<n; i++){
        int key = i+1;
        HashMap<Integer,Integer> value = new HashMap<>();
        value.put(sc.nextInt(),sc.nextInt());
        map.put(key,value);
    }
    Integer max = findMax(map,v,n);
    System.out.println(max);
}
private static Integer findMax(Map<Integer, Map<Integer, Integer>> map, int v, int n) {
    //数据初始化
    int[][] arr = new int[n+1][v+1];
    for(int i=0;i<v+1;i++){
        arr[0][i]=0;
    }
    for(int i=0;i<n+1;i++){
        arr[i][0]=0;
    }
    int max = 0;//背包最大值
    int pack = v;//当前背包容量
    Set<Map.Entry<Integer, Map<Integer, Integer>>> entries = map.entrySet();
    for (Map.Entry<Integer, Map<Integer, Integer>> entry : entries) {
            int i = entry.getKey();//当前商品编号
            Map<Integer,Integer> Value =entry.getValue();//当前商品价值
            Set<Map.Entry<Integer, Integer>> entries1 = Value.entrySet();
            int tiji ;//当前商品体积
            int value;//当前商品价值
            for (Map.Entry<Integer, Integer> integerIntegerEntry : entries1) {
                tiji = integerIntegerEntry.getKey();
                value = integerIntegerEntry.getValue();
                for (int j = 1; j < v + 1; j++) {
                    if (j < tiji) {
                        arr[i][j] = arr[i - 1][j];
                    } else {
                        arr[i][j] = Math.max(arr[i - 1][j], arr[i - 1][j - tiji] + value);
                        if (max <= arr[i][j]) {
                            max = arr[i][j];
                        }
                    }
                }
            }
    }
    return  max;
}
}
  • 10
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值