如何带出三角洲安全箱中的最高价值物品?——回溯与动态规划解析

三角洲安全箱如何带出最高价值物品?

——回溯与动态规划解析 0/1背包问题

情景如下,不论时航天总裁还是巴克什的巴别塔,作为跑刀鼠的你前有狼、后有虎、马上被逮捕。太令人绝望了,不过在你被当作路边的一条踢死之前,你发现了很多盒子与物资,竟然有所有的大红物资(各一个,九格大红与非洲之星情况特殊,不做考虑 .PS你也摸不到)。恰好你有九格安全箱,但是马上被踢死了你必须在安全箱容量受限的情况下,带出最高价值的物品,所以还在犹豫什么?!快吃!!!(答案在文章结尾

这是一个经典的0/1 背包问题,我们可以用回溯算法动态规划两种方式来解决。本文将详细介绍这两种方法,并附上完整的代码示例。


物资列表

物品编号 物品名称 体积(格数) 价值(信用点)
0 黄金瞪羚 4 432582
1 摄影机 4 731917
2 显卡 2 339428
3 军用无人机 4 534054
4 军用控制终端 2 395967
5 呼吸机 4 466235
6 奥莉薇娅香槟 2 339555
7 实验数据 1 234329
8 量子存储 1 276319
9 棘龙爪化石 2 353088
10 万足金条 2 332511
11 赛伊德的怀表 1 216781
12 军用信息终端 6 913372
13 名贵机械表 1 211607
14 天圆地方 4 493952
15 军用电台 4 1250955
16 飞行记录仪 6 1970255
17 军用炮弹 6 1451609
18 克劳狄乌斯半身像 6 1312452
19 步战车模型 6 1349690
20 自动除颤仪 6 1371940
21 强力吸尘器 6 1435429
22 笔记本电脑 6 3320020
23 云存储阵列 6 2212318
24 雷斯的留声机 6 1264082
25 医疗机器人 6 1388236
26 装甲车电池 6 1380874
(价值来源于官方,交易行波动请自行斟酌)

一、 回溯算法求解

回溯思路

回溯法采用递归穷举,尝试所有可能的选择,并在每次选择后递归进入下一层决策。如果发现当前路径不可能超过已知的最大值,则回溯。

说人话就是,把每一种保险箱放物资的方案都试一遍,就能找出最大价值的方案了。

public class backpack {
    static int maxValue = 0;  // 记录最大价值
    static List<Integer> bestChoice = new ArrayList<>(); // 记录最佳选择
    public static void main(String[] args) {
        String[] name0 = {"黄金瞪羚","摄影机","显卡","军用无人机","军用控制终端","呼吸机","奥莉薇娅香槟","实验数据","量子存储","棘龙爪化石","万足金条","赛伊德的怀表", "军用信息终端","名贵机械表","天圆地方"};
        int[][] wuZi0 = {{4, 432582}, {4, 731917}, {2, 339428}, {4, 534054}, {2, 395967}, {4, 466235}, {2, 339555}, {1, 234329}, {1, 276319}, {2, 353088}, {2, 332511}, {1, 216781}, {6, 913372}, {1, 211607}, {4, 493952}};

        String[] name1 = {"军用电台","飞行记录仪","军用炮弹","克劳狄乌斯半身像","步战车模型","自动除颤仪","强力吸尘器","笔记本电脑","云存储阵列","雷斯的留声机","医疗机器人","装甲车电池"};
        int[][] wuZi1 = {{4, 1250955}, {6, 1970255}, {6, 1451609}, {6, 1312452}, {6, 1349690}, {6, 1371940}, {6, 1435429}, {6, 3320020}, {6, 2212318}, {6, 1264082}, {6, 1388236}, {6, 1380874}};
        // 合并 name 数组
        String[] name = new String[name0.length + name1.length];
        System.arraycopy(name0, 0, name, 0, name0.length);
        System.arraycopy(name1, 0, name, name0.length, name1.length);
        // 合并 wuZi 数组
        int[][] wuZi = new int[wuZi0.length + wuZi1.length][2];
        System.arraycopy(wuZi0, 0, wuZi, 0, wuZi0.length);
        System.arraycopy(wuZi1, 0, wuZi, wuZi0.length, wuZi1.length);

        List<Integer> tempList = new ArrayList<>();
        //------------------------------------------------------------------------------------------------------------
        backTracking(0, 0, wuZi, 9, tempList);      //回溯算法,进入递归
        //------------------------------------------------------------------------------------------------------------
        
        System.out.println("最大价值:" + maxValue);
        System.out.print("最优选择物品:");
        for(Integer i : bestChoice){
            System.out.print(name[i]+ " ");
        }

    }
	//--------------------------------注意看这里---------------------------------
    static void backTracking(int index, int sum, int[][] wuZi, int remainSpace, List<Integer> currentList) {
        if (remainSpace < 0) {
            return;
        }
        if (sum > maxValue) { //如果物资价值总和大于现在记录的最大值则替换
            maxValue = sum;
            bestChoice = new ArrayList<>(currentList); // 更新最优选择方案
        }
        for (int i = index; i < wuZi.length; i++) {
            currentList.add(i);  // 选择当前物品放进保险箱,进入下一层递归
            backTracking(i + 1, sum + wuZi[i][1], wuZi, remainSpace - wuZi[i][0], currentList);  //下一层放入拿出
            currentList.remove(currentList.size() - 1); // 由下一层返回上来,把放进去的物资拿出来(回溯)
        }
    }
}

二、 动态规划求解

1. 状态定义

我们定义 dp[j] 表示:

在容量为 j 时能获得的最大价值


2. 状态转移方程

对于每个物品 i,有 两种选择

  • 不选 物品 idp[j] 保持不变

  • 物品 i:当前容量 j 需要至少 wuZi[i][0] 才能放下这个物品,我们从 dp[j - weight] 加上当前物品的 value,即:

    \[dp[j] = \max(dp[j], dp[j - weight] + value) \]

注意

  • 要倒序遍历 j,从 capacity(最大容量)到 weight(最小容量),这样保证 dp[j - weight] 在本轮迭代前未被覆盖。
  • 这样可以避免使用二维 dp 数组,减少空间复杂度
public class BackpackDP {
    public static void main(String[] args) {
        String[] name0 = {"黄金瞪羚", "摄影机", "显卡", "军用无人机", "军用控制终端", "呼吸机", "奥莉薇娅香槟", "实验数据", "量子存储", "棘龙爪化石", "万足金条", "赛伊德的怀表", "军用信息终端", "名贵机械表", "天圆地方"};
        int[][] wuZi0 = {{4, 432582}, {4, 731917}, {2, 339428}, {4, 534054}, {2, 395967}, {4, 466235}, {2, 339555}, {1, 234329}, {1, 276319}, {2, 353088}, {2, 332511}, {1, 216781}, {6, 913372}, {1, 211607}, {4, 493952}};

        String[] name1 = {"军用电台", "飞行记录仪", "军用炮弹", "克劳狄乌斯半身像", "步战车模型", "自动除颤仪", "强力吸尘器", "笔记本电脑", "云存储阵列", "雷斯的留声机", "医疗机器人", "装甲车电池"};
        int[][] wuZi1 = {{4, 1250955}, {6, 1970255}, {6, 1451609}, {6, 1312452}, {6, 1349690}, {6, 1371940}, {6, 1435429}, {6, 3320020}, {6, 2212318}, {6, 1264082}, {6, 1388236}, {6, 1380874}};

        // 合并 name 数组
        String[] name = new String[name0.length + name1.length];
        System.arraycopy(name0, 0, name, 0, name0.length);
        System.arraycopy(name1, 0, name, name0.length, name1.length);

        // 合并 wuZi 数组
        int[][] wuZi = new int[wuZi0.length + wuZi1.length][2];
        System.arraycopy(wuZi0, 0, wuZi, 0, wuZi0.length);
        System.arraycopy(wuZi1, 0, wuZi, wuZi0.length, wuZi1.length);

        int capacity = 9; // 背包容量
        int n = wuZi.length; // 物品数量
        
        // dp[j] 代表容量为 j 的时候能获得的最大价值
        int[] dp = new int[capacity + 1];
        boolean[][] path = new boolean[n][capacity + 1]; // 记录选择路径
        
        // 动态规划求解
        for (int i = 0; i < n; i++) {
            int weight = wuZi[i][0];
            int value = wuZi[i][1];
            
            for (int j = capacity; j >= weight; j--) { // 倒序遍历,避免覆盖
                if (dp[j] < dp[j - weight] + value) {
                    dp[j] = dp[j - weight] + value;
                    path[i][j] = true; // 记录选择该物品
                }
            }
        }
        
        // 反向查找选择的物品
        List<Integer> bestChoice = new ArrayList<>();
        int remainingWeight = capacity;
        for (int i = n - 1; i >= 0; i--) {
            if (remainingWeight >= wuZi[i][0] && path[i][remainingWeight]) {
                bestChoice.add(i);
                remainingWeight -= wuZi[i][0];
            }
        }
        
        // 输出结果
        System.out.println("最大价值:" + dp[capacity]);
        System.out.print("最优选择物品:");
        for (Integer i : bestChoice) {
            System.out.print(name[i] + " ");
        }
    }
}


3. 理解

  • 想象你在整理“仪容仪表”,每次面对一个新的物资,你有两个选择:
    1. 不拿这个物品,那么你的背包价值和之前是一样的。
    2. 拿这个物品,那么你需要腾出它的背包格数,并获得它的价值。

dp[j]数组就是记录当有j格保险箱(背包)时,放什么最值钱。


4. 对比

算法性能对比

算法类型 适用场景 时间复杂度 适用数据规模
回溯 物品较少,想要所有解 指数级 (O(2^n)) 小规模问题
动态规划 物品较多,求最优解 O(n * capacity)也就是(O(n)) 大规模问题

5. 总结

通过本文,我们了解了如何利用回溯算法动态规划两种方式解决三角洲安全箱的最优物品选择问题。两种方法各有优劣,适用于不同的应用场景。

无论哪种方法,我们都能成功带走三角洲安全箱中价值最高的物品!谁叫你有保险箱呢╮(╯▽╰)╭🚀

答案揭晓:(所以九格大红应该大于4047449才会优先选择,当然了你现在还在做梦能捡到这么多红)愚人节快乐!!!😏

最大价值:4047449
最优选择物品:笔记本电脑 赛伊德的怀表 量子存储 实验数据

希望这篇博客能帮助你理解回溯与动态规划的核心思想!🎯

posted @ 2025-04-01 20:06  发光的反派  阅读(474)  评论(0)    收藏  举报