背包问题(贪心)& 二维01背包问题

背包问题(贪心)

最优装载问题

题目描述

有n件物品和一个最大承重为w 的背包。第i件物品的重量是weighti,每件只能用一次,求装入背包的最多物品数量。

题目分析

  • 因为我们只要求装入物品的数量 ,所以装重的显然没有装轻的划算
  • 因此将数组weighti按从小到大排序依次选择每个物品,直到装不下为止,就可以得到装入背包的最多物品数量。

代码

java 复制代码
public static int stone(int n, int w, int[] weight) {
	Arrays.sort(weight);    //将weight数组从小到大排序
    int max = 0;
    int num = 0;
    for(int i = 0; i < n; i++) {
        if(max + weight[i] <= w){
        max += weight[i];
           num += 1;
       }
    }
    return num;
}

01背包问题

与上面的贪心背包问题而言,贪心背包问题中物品的价值就是它的重量。

先前题主做的拿金币问题,也可以说是一道背包问题,不过其中背包的容量是无限的,物品就是金币。

题目描述

有n件物品和一个最大承重为bagweight 的背包。第i件物品的重量是weighti,得到的价值是valuei 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
卡码网第46题

题目分析

显然也是一道动态规划题,也就是说背包问题是动态规划问题的子集,当然这不重要。

我们先观察一下背包的属性:

  • 当前装入物品的个数
  • 当前装入物品的总重量(容量)
  • 当前装入物品的总价值
  • 关键在当前重量的基础上使得价值最高

注:下文基本转述于代码随想录的网站,只加了部分自己的理解

可转到代码随想录的网站去更深刻地理解01背包知识

代码随想录的链接:戳这里进入

老规矩动归五部曲

定义dp数组

定义一个二维数组dpij,将i定义为当前放入了多少个物品,表示

  • 下标为0-i的物品里任意取,
  • 放进容量为j的背包
  • 价值总和最大是多少。(dp数组的值)

确定dp数组递归公式

在决定是否放入第i个物品时,显然有两种情况

  • 要么不满足放入第i个物品的情况(当物品i的重量大于背包j的重量时,物品i无法放进背包中)
java 复制代码
dp[i][j] = dp[i-1][j];
  • 要么满足放入第i个物品的情况,此时比较放入前后的价值总和,判断是否要放入。
java 复制代码
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j - weight[i] + value[i];

dp数组的初始化

从dpij出发,

  1. 当背包容量j为0的时候,则放不下任何物品,背包中价值总和为0;
java 复制代码
for (int i = 0 ; i < weight.length; i++) { 
    dp[1][0] = 0;

再看其他情况。

  1. 因为递推公式dpij = max(dpi - 1j, dpi - 1j - weight\[i] + valuei);
    当中含有i-1 ,即我们在遍历时肯定从i=1的时候开始遍历,那么物品为0的时候就一定要初始化。
    此时考虑是否可以加入物品0,
    • 当 j < weight0的时候,dp0j 为 0
    • 当j >= weight0时,dp0j 为value0
java 复制代码
for (int j = 0 ; j < weight[0]; j++)   // 如果把dp数组预先初始化为0了,这一步可以省略
    dp[0][j] = 0;
    
for (int j = weight[0]; j <= bagweight; j++) 
    dp[0][j] = value[0];
  1. 其他位置可以任意初始化,但是一开始就统一把dp数组统一初始为0,更方便一些。

dp数组的遍历顺序

显然有两个遍历的维度:物品与背包容量

先遍历物品,或先遍历背包容量呢。

这里两种遍历顺序都可以,是因为,递推的方向分别是由上的与由左的到,而两个遍历顺序都是会先得到递推公式需要的旧数据,因此不影响。

java 复制代码
//先遍历物品
for(int i = 1; i < weight.size(); i++) { // 遍历物品
    for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
        if (j < weight[i]) dp[i][j] = dp[i - 1][j];
        else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

    }
}
//先遍历背包容量
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
    for(int i = 1; i < weight.size(); i++) { // 遍历物品
        if (j < weight[i]) dp[i][j] = dp[i - 1][j];
        else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
    }
}

举例说明dp数组

代码

java 复制代码
import java.util.Arrays;

public class BagProblem {
    public static void main(String[] args) {
        int[] weight = {1,3,4};//这里是代码随想录的示范用例
        int[] value = {15,20,30};
        int bagSize = 4;
        testWeightBagProblem(weight,value,bagSize);
    }

    public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){

        // 创建dp数组
        int goods = weight.length;  // 获取物品的数量
        int[][] dp = new int[goods + 1][bagSize + 1];  // 给物品增加冗余维,i = 0 表示没有物品可选

        // 初始化dp数组,默认全为0即可
        // 填充dp数组
        for (int i = 1; i <= goods; i++) {
            for (int j = 1; j <= bagSize; j++) {
                if (j < weight[i - 1]) {  // i - 1 对应物品 i
                    /**
                     * 当前背包的容量都没有当前物品i大的时候,是不放物品i的
                     * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
                     */
                    dp[i][j] = dp[i - 1][j];
                } else {
                    /**
                     * 当前背包的容量可以放下物品i
                     * 那么此时分两种情况:
                     *    1、不放物品i
                     *    2、放物品i
                     * 比较这两种情况下,哪种背包中物品的最大价值最大
                     */
                    dp[i][j] = Math.max(dp[i - 1][j] , dp[i - 1][j - weight[i - 1]] + value[i - 1]);  // i - 1 对应物品 i
                }
            }
        }

        // 打印dp数组
        for(int[] arr : dp){
            System.out.println(Arrays.toString(arr));
        }
    }
}
相关推荐
梓䈑42 分钟前
【算法题攻略】BFS 解决FloodFill 算法、最短路问题、多源BFS 和 拓扑排序
c++·算法·宽度优先
Adios7943 小时前
设置交集大小至少为2
数据结构·算法·leetcode
程序喵大人5 小时前
【C++进阶】STL容器与迭代器 - 01 STL 容器先解决元素放在哪里
开发语言·c++·stl
cui_ruicheng6 小时前
Python从入门到实战(十六):多进程编程
开发语言·python
Jelena157795857926 小时前
电商运营分析数据比价接口实战:多平台价格监控与智能决策系统
java·大数据·数据库
wdfk_prog7 小时前
嵌入式面试真题第 15 题:不可恢复异常后的通用崩溃快照、调用栈保存与离线分析架构
linux·开发语言·面试·架构
晴空了无痕7 小时前
从 Go 基础到 K8s:一条可落地的 Go 服务端成长路线
开发语言·后端·golang·kubernetes
神明不懂浪漫7 小时前
【第五章】Java中的继承与多态
java·开发语言
神州世通8 小时前
解密企业通信安全防线:Avaya Aura 三层安全架构深度解析
开发语言·php