0-1背包问题(回溯法)

本文介绍了如何利用回溯法解决经典的0-1背包问题,详细阐述了回溯法的概念,并通过建立二叉树模型展示了算法过程。在代码实现部分,给出了Java代码示例,通过递归回溯找到使背包中物品总价值最大化的解决方案。最后,强调了不断解决问题和学习新方法的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在之前的博客中,我写过用动态规划解决的0-1背包问题(具体可查看动态规划(0-1背包问题)),但是在最近刷软考题的时候,发现回溯法也可以解决0-1背包问题,对于回溯法我不是很了解,于是在学习了回溯法之后,我有用回溯法写了0-1背包的算法过程

0-1背包问题

给定n种物品和一背包。物品 i 的重量为 w[i],其价值为 v[i],背包的容量为 c。问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

回溯法

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
我们可以将之理解成,把问题转换成一棵树,通过深度优先搜索的方式,将所有的叶子节点都搜索一遍,在搜索的过程中,我们添加一些限定条件,把不符合条件的节点直接排除,以达到最终的算法目的。当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

问题分析

对于有n种物品的 0-1 背包问题,其解空间由长度为n的 0-1 向量组成,该解空间包含了对变量的所有可能的0-1 赋值。当 n=3 时,其解空间是{ (0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1) }
对于n= 3的0-1 背包问题,其解空间可以用一棵完全二叉树表示,从树根到叶子结点的任意一条路径可表示解空间中的一个元素, 如从根结点A到结点J的路径对应于解空间中的一个元素(1, 0, 1)。

运用回溯法解题的关键要素有以下三点:

  1. 针对给定的问题,定义问题的解空间;
  2. 确定易于搜索的解空间结构;
  3. 以深度优先方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。

建立二叉树

我们建立一个简单的二叉树模型

	 *        A
     *      /    \
     *    Y       N          X[0]
     *   / \     / \
     *  Y   N    Y   N        X[1]
     * /\  /\   /\   /\
     * Y N Y N  Y N  Y N       X[2]
     * 8 7 6 5  4 3  2 1
  • 通过上面的二叉树我们可以进行问题分析:X[i]表示第i件物品,Y/N代表是否选择该物品
  • 当我们三件物品都不选择的时候就来到了1处,相应的我们就可以计算出1处对应的物品价值
  • 这时我们回溯到X[1](第二件物品处),我们选择取第三件物品,这时我们就来到了2处,得出2处的价值后,我们就可以将两处比较,留下价值高的
  • 然后再回溯到第一件物品处,选择取第二件物品不取第三件物品,就来到3处,相应的得出价值,进行比较

如此反复可以得到每一处所取物品对应的价值,我们再在其中添加相应的约束条件,所选物品重量相加不能超出背包容量,这样就可以进行剪枝操作

代码实现

通过上述的问题分析,我们就可以开始解决代码部分了

public class KnapsackProblem_2 {
    //0-1背包问题(回溯算法)
    //给定n种物品和一背包。物品i的重量是wi,其价值为pi,背包的容量为C。 问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
    
    static int MaximumValue = 0; // 最大价值;当前的最大价值,初始化为0
    static int CurWeight = 0; // 当前放入背包的物品总重量
    static int CurValue = 0; // 当前放入背包的物品总价值


    public static int backtrack(int t, int[] W, int[] v, int C) {
        int N = W.length;// 物品数量
        int[] x = new int[N];// x[i]=1代表物品i放入背包,0代表不放入
        for (int i = 0; i < N; i++) {//背包置空
            x[i] = 0;
        }
        // 如果是子节点 当前价值和最佳价值做判断 保存最佳价值
        if (t > N - 1) {
            if (CurValue > MaximumValue) {
                MaximumValue = CurValue;
            }
            return MaximumValue;
        }
        // 如果不是子节点 对子节点进行遍历
        else {
            // 就两种情况 取或不取 用0/1表示
            for (int i = 0; i <= 1; i++) {
                x[t] = i;
                if (i == 0) {
                    // 如果是不取 就不需要进行判断 直接到下一个节点
                    backtrack(t + 1, W, v, C);
                } else
                // 放入背包就进行约束条件 判断放入背包的东西是否合法
                {
                    if (CurWeight + W[t] <= C) {
                        CurWeight += W[t];
                        CurValue += v[t];
                        // 当东西装进入背包后你可以进行对下个商品的判断了
                        backtrack(t + 1, W, v, C);
                        //能执行以下两个语句就说明你回溯到了上一个节点 所以你就需要恢复现场 把你刚刚拿的东西退出来 我们要冲上一个节点又要重新来遍历 如果不减你就会多加一遍
                        CurWeight -= W[t];
                        CurValue -= v[t];
                    }
                }
            }
        }
        return MaximumValue;
    }

}

代码完成后我们现在来测试一下

public static void main(String[] args) {
        int[] W = {1, 4, 3}; // 每个物品的重量
        int[] v = {1500, 4000, 3000};// 每个物品的价值
        int C = 4;//背包容量
        backtrack(0, W, v, C);
        System.out.println(MaximumValue);
    }

运行结果:
在这里插入图片描述
结果正确!

结语:

学习算法的过程是很漫长的,当你解决了一个问题的时候,不妨多想想有没有别的方法可以解决这个问题,只有反复解决新的问题,勤加练习,多多思考,多向他人请教,才能有更多收获。

<think>嗯,用户想了解Excel中的VLOOKUP函数的用法和参数含义。首先,我需要回忆一下VLOOKUP的基本结构。VLOOKUP是垂直查找函数,用于在表格的首列查找指定的值,然后返回该行中指定列的数据。它的四个参数分别是查找值、表格范围、列索引号和匹配模式。 接下来,我得确认每个参数的具体作用。第一个参数是lookup_value,也就是用户要查找的值,比如产品ID或者姓名。第二个参数是table_array,即查找的范围,这里要注意的是,查找值必须位于这个范围的第一列,否则函数无法正确工作。第三个参数是col_index_num,也就是用户想要返回的数据在表格范围中的第几列,这个列数是从表格范围的第一列开始算起的,不是整个工作表的列。第四个参数是range_lookup,决定是精确匹配还是近似匹配,通常用FALSE或0来指定精确匹配,避免错误。 然后,用户可能需要一个具体的例子来理解如何使用。比如,假设有一个产品价格表,用户想根据产品ID查找对应的价格。这时候,VLOOKUP就可以在ID列查找,然后返回价格列的数据。同时,要提醒用户注意表格范围最好使用绝对引用,比如$A$2:$C$100,这样在拖动公式时范围不会变化。 可能遇到的问题也需要考虑。比如,如果查找值不在表格的第一列,或者列索引号超过了表格的列数,函数会返回错误。此外,数据格式不一致也可能导致问题,比如查找值是数字,而表格中是文本格式的数字,这时候需要统一格式。还有,当出现#N/A错误时,可能是查找值不存在,或者匹配模式设置不正确,需要检查参数是否正确。 用户可能还会问到如何避免常见的错误,比如部分数据匹配出错的情况。这时候需要检查是否有多余的空格,或者数据格式不一致,或者表格范围没有锁定导致拖动时偏移。此外,XLOOKUP作为VLOOKUP的升级版,可能在某些情况下更方便,比如可以向左查找或者返回多列数据,这也是可以提及的内容。 最后,整理步骤,从基础语法到参数说明,再到具体案例,逐步引导用户理解如何使用VLOOKUP函数。同时,提供常见错误和解决方法,帮助用户在实际应用中避免问题。如果有必要,还可以比较VLOOKUP和其他查找函数的区别,比如HLOOKUP或XLOOKUP,但根据用户的问题,主要集中在VLOOKUP本身即可。</think>### Excel VLOOKUP函数详解 #### 一、基础语法与参数说明 VLOOKUP函数的语法为: $$=VLOOKUP(lookup\_value, table\_array, col\_index\_num, [range\_lookup])$$ 包含4个参数: 1. **lookup_value**(必填):要查找的值(如单元格引用或具体值) 2. **table_array**(必填):包含数据的表格范围(必须包含查找列和返回列) 3. **col_index_num**(必填):返回值所在列的序号(从table_array第一列开始计数) 4. **range_lookup**(可选):匹配类型 - `TRUE`/`1`:近似匹配(默认值,需数据升序排列) - `FALSE`/`0`:精确匹配(常用选项) [^1][^2] #### 二、使用步骤演示(工资表查询案例) 假设需要根据员工编号查询工资: 1. 建立查询单元格(如`B12`) 2. 输入公式: ```excel =VLOOKUP(A12, $A$2:$D$100, 4, 0) ``` - `A12`:待查询的员工编号 - `$A$2:$D$100`:锁定数据区域(绝对引用) - `4`:返回第4列(工资列) - `0`:精确匹配 [^2][^3] #### 三、常见错误与解决方法 | 错误现象 | 原因 | 解决方案 | |---------|------|---------| | #N/A | 查找值不存在 | 检查数据源或改用`IFERROR`容错 | | #REF! | 列序号超出范围 | 确认col_index_num ≤ 表格列数 | | 部分匹配失败 | 数据格式不一致 | 统一数值/文本格式 | | 结果错位 | 表格未锁定 | 使用`$`符号固定区域引用 | [^3][^4] #### 四、进阶技巧 1. **多条件查询**: 使用辅助列合并多个条件字段 ```excel =VLOOKUP(A2&B2, $D$2:$F$100, 3, 0) ``` 2. **通配符匹配**: `"*"`匹配任意字符,`"?"`匹配单个字符 ```excel =VLOOKUP("张*", $A$2:$C$100, 3, 0) ``` 3. **跨表查询**: 引用其他工作表数据 ```excel =VLOOKUP(A2, Sheet2!$A$2:$D$100, 4, 0) ``` [^1][^4]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没谱的曲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值