背包问题(1)

大一学了 大三复习一下巩固一下知识;

装箱问题_牛客题霸_牛客网https://www.nowcoder.com/practice/55100a6608ad4656849dbd1f16d044cb?tpId=308&tqId=170605&sourceUrl=%2Fexam%2Foj%3FquestionJobId%3D10%26subTabName%3Donline_coding_page

实例

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

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int a = in.nextInt();
            int b = in.nextInt();
            System.out.println(a + b);
        }
    }
}

第一次试错(贪心):

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

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
    public static void main(String[] args) {
        //TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
        // to see how IntelliJ IDEA suggests fixing it.
        Scanner scanner = new Scanner(System.in);
        //箱子容量
        int boxv= scanner.nextInt();
        //物品数量
        int object_num = scanner.nextInt();
        int[] object = new int[object_num];
        for(int i = 0; i < object_num; i++){
            object[i] = scanner.nextInt();
        }
        int res = boxv;
        for(int i = 0; i < object_num; i++){
            if (boxv-object[i] > 0) {
                boxv = Math.min(boxv - object[i], boxv);
            }
        }
        System.out.println(boxv);
        }
    }

错误之处 贪心思想只能解决局部最优 无法结局全局最优

解题思路

动态规划

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

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
    public static void main(String[] args) {
        //TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
        // to see how IntelliJ IDEA suggests fixing it.
        Scanner scanner = new Scanner(System.in);
        //箱子容量
        int boxv= scanner.nextInt();
        //物品数量
        int object_num = scanner.nextInt();
        int[] object = new int[object_num+1];
        for(int i = 0; i < object_num; i++){
            object[i] = scanner.nextInt();
        }
        int[] dp = new int[boxv+1];//盒子中的体积
        for(int i = 0; i < object_num; i++){
            //每个物品只使用一次 倒序遍历
           for (int j = boxv; j >= object[i]; j--){
              dp [j] = Math.max(dp[j],dp[j-object[i]]+object[i]);
           }
        }
        System.out.println(boxv-dp[boxv]);
        }
    }
  • 状态定义
    dp[j] 表示容量为 j 时能装入的最大总体积

    这个定义直接对应问题目标:我们需要的最终结果就是 dp[V](容量为 V 时的最大装入体积)。

回溯剪枝

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

public class Main {
    static int[][] f; // 记忆化数组

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int V = scanner.nextInt(); // 箱子容量
        int n = scanner.nextInt(); // 物品数量
        int[] v = new int[n];      // 物品体积数组

        // 读取每个物品的体积
        for (int i = 0; i < n; i++) {
            v[i] = scanner.nextInt();
        }

        // 初始化记忆化数组,-1表示未计算
        f = new int[n + 1][V + 1];
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= V; j++) {
                f[i][j] = -1;
            }
        }

        // 计算最大能装入的体积
        int maxVolume = dp(v, V, n);
        // 输出剩余空间
        System.out.println(V - maxVolume);
    }

    // 递归函数:计算前n个物品,容量为V时的最大装入体积
    static int dp(int[] v, int V, int n) {
        // 如果已经计算过,直接返回结果
        if (f[n][V] >= 0) {
            return f[n][V];
        }

        // 边界条件:没有物品时,最大体积为0
        if (n == 0) {
            return 0;
        }

        // 当前物品体积小于等于容量,可以选或不选
        if (V >= v[n - 1]) {
            f[n][V] = Math.max(dp(v, V, n - 1),
                    dp(v, V - v[n - 1], n - 1) + v[n - 1]);
        } else {
             // 当前物品体积大于容量,不能选
            
            f[n][V] = dp(v, V, n - 1);
        }

        return f[n][V];
    }
}    
  • 状态转移逻辑

    对于每个物品体积 w,有两种选择:

    • 不选 :则容量为 j 时的最大体积仍为 dp[j]
    • :前提是 j≥w,此时需要用前 i−1 个物品填满 j−w 的容量,再加上当前物品的体积 w,即 dp[j-w] + w
      因此,状态转移方程为:dpj=max(dpj, dpj−w+w)
      逆序遍历容量(从 V 到 w)是为了确保每个物品只被选一次(避免重复计算),这是 01 背包的核心优化技巧。
  1. 回溯法(带剪枝):通过递归枚举所有可能的物品组合,并在当前体积超过容量时提前终止(剪枝)。对物品体积从大到小排序可以更快触发剪枝条件。
  2. 记忆化搜索:递归实现动态规划,通过记忆化数组避免重复计算,代码结构更直观。
相关推荐
Evand J12 分钟前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪
杜大哥34 分钟前
python程序:如何查看电脑【电池电量的剩余百分比】 和 【是否插入连接着充电器】?
开发语言·python
wuyk55535 分钟前
Python网络爬虫入门到实战 第01章:爬虫到底是什么?原理、流程、合法性、风险全解析(零基础必看)
开发语言·爬虫·python
信誓旦旦的程序猿41 分钟前
【量化系统从零构建 #04】存储设计:选型·建库·交易日历
java·人工智能·python·股票数据api·股票数据·股票数据api接口·股票api数据接口
yurenpai(27届找实习中)42 分钟前
Java 10 的 var 为什么不能随便用?从订单汇总看懂类型推断与泛型陷阱
java·开发语言·java18
shmily麻瓜小菜鸡1 小时前
JS/TS 易踩坑知识点 — 模块化与工程化类
开发语言·javascript·ecmascript
geovindu1 小时前
CSharp: Observer Pattern
开发语言·后端·观察者模式·设计模式·c#·.netcore·行为模式
Flynt2 小时前
把公司项目迁到 Spring Boot 4.0:编译通过只是开始
java·spring boot·后端
郝学胜-神的一滴2 小时前
C++11 工程级应用 09:告别无谓拷贝,解锁高性能移动语义
开发语言·数据结构·c++·vscode·软件工程·visual studio
Tangyuewei2 小时前
给老 Spring 项目装个 AI Agent
java·人工智能·spring