- 此为课题组所指导本科生和低年级硕士生学习组合优化问题汇报
- 所用教材:北京大学屈婉玲教授《算法设计与分析》
- 课程资料:https://www.icourse163.org/course/PKU-1002525003
- 承诺不用于任何商业用途,仅用于学术交流和分享
- 更多内容请关注
许志伟
课题组官方中文主页:https://JaywayXu.github.io/zh-cn/
1. 连续邮资问题 (10.6)
1.1 问题定义
在连续邮资问题中,给定了 \(n\) 种不同面值的邮票,每个信封至多贴 \(m\) 张邮票。我们的目标是设计一个邮票面值集合,使得从面值 \(1\) 开始,能够连续支付的邮资区间尽可能大。
-
输入条件:
- \(n = 5\):邮票种类数为 5。
- \(m = 4\):每个信封最多可以贴 4 张邮票。
-
目标:
- 在给定邮票面值的设计下,从 \(1\) 开始,所有整数邮资都能用邮票贴出,且所能覆盖的连续区间最大。
-
问题示例:
-
设计1:邮票面值为 \(\{1, 3, 11, 15, 32\}\)。
- 解释:
- 使用最多 4 张邮票进行组合,可以覆盖邮资区间 1 到 70,即可以支付所有从 1 到 70 的邮资。
- 例如:
- 只用面值为 \(1\) 的邮票:最多贴出 \(4 \times 1 = 4\)。
- 使用邮票面值 \(1\) 和 \(3\):如 \(3 + 3 + 1 = 7\)。
- 使用面值 \(11, 15\),可组合成更大面值。
- 这个设计成功地贴出了1 到 70 的连续邮资,因此是一个好的设计。
- 解释:
-
设计2:邮票面值为 \(\{1, 6, 10, 20, 30\}\)。
- 解释:
- 使用最多 4 张邮票,无法贴出所有从 1 开始的邮资。例如:
- 用 4 张面值 \(1\) 的邮票最多贴出 \(4\)。
- 想要贴出 \(5\) 时,必须要有四张面值为 \(1\) 的邮票,无法贴出 5。
- 因此,设计2 无法贴出 完整的连续邮资,是一个不好的设计。
- 使用最多 4 张邮票,无法贴出所有从 1 开始的邮资。例如:
- 解释:
-
1.2 搜索空间与约束条件
1.2.1 搜索空间
在连续邮资问题的算法设计中,可行解的搜索空间是 邮票面值的所有组合,并满足以下条件:
- 每个可行解表示为一个 向量:
$ (x_1, x_2, \dots, x_n) $
其中,\(x_1\) 一定是 \(1\),因为面值 1 是确保支付连续区间的最小单位。 - 向量中的面值是 严格递增,即
\(x_1 < x_2 < \dots < x_n\)。 - 每个信封上最多可以贴 \(m\) 张邮票,用以支付指定的邮资。
1.2.2 约束条件
为了确保连续邮资区间最大化,每个邮票面值的选取需要满足以下约束:
-
第一个面值的固定性:
- \(x_1 = 1\),因为任何可行的邮资设计都必须能支付 1 单位邮资。
-
后续面值的选取范围:
- 设 \(r_i\) 表示使用 \(i\) 种邮票时,能够贴出的最大连续邮资区间的上界。
- 下一个面值 \(x_{i+1}\) 的选取必须满足:\[x_{i+1} \in [x_i + 1, r_i + 1] \]
- 解释:
- \(x_{i+1} \geq x_i + 1\):因为邮票面值递增。
- \(x_{i+1} \leq r_i + 1\):保证所有 \(r_i + 1\) 以内的邮资能够支付。若 \(x_{i+1} > r_i + 1\),则 \(r_i + 1\) 单位邮资将无法支付。
-
最大化邮资区间的策略:
- 对于每一层的搜索,当选定了 \(i\) 种邮票的面值 \((x_1, \dots, x_i)\) 后,应确保最大化邮资区间 \([1, r_i]\),使得在这个区间内的每个邮资都能用 \(i\) 种邮票的组合支付。
1.2.3 举例说明约束
-
假设前两种邮票面值:
\(x_1 = 1\),\(x_2 = 2\)。- 使用最多 4 张邮票时,可以覆盖的邮资区间为 \([1, 6]\),即:
- \(1 = 1\)
- \(2 = 2\)
- \(3 = 1 + 2\)
- \(4 = 2 + 2\)
- \(5 = 1 + 2 + 2\)
- \(6 = 2 + 2 + 2\)
- 使用最多 4 张邮票时,可以覆盖的邮资区间为 \([1, 6]\),即:
-
选择第三种邮票面值的范围:
- 由于 \(r_2 = 6\),因此第三种邮票的面值 \(x_3\) 必须满足:\[x_3 \in [3, 7] \]
- 例如,若 \(x_3 = 3\),则可以使用 \(x_1, x_2, x_3\) 组合来支付更大的连续邮资区间。
- 由于 \(r_2 = 6\),因此第三种邮票的面值 \(x_3\) 必须满足:
1.3 上界 \(r_i\) 的计算
1.3.1 \(r_i\) 的含义
根据上述描述,\(r_i\) 是 前 \(i\) 种邮票所能支付的最大连续邮资区间的上界,即:
- 贴到 \(j\) 的邮资时,使用的邮票数量不超过 \(m\) 张。
- 当尝试贴邮资 \(j + 1\) 时,需要的邮票数量超过 \(m\) 张,此时 \(j\) 就是 断点。
这一部分的目标是计算这个最大连续的邮资区间上界 \(r_i\),即从 1 开始的 最大连续邮资区间 能延伸到哪里。
1.3.2 \(y_i(j)\) 与上界的逻辑关系
在计算 \(r_i\) 时,我们重点依赖于 递归函数 \(y_i(j)\),这个函数表示:
使用前 \(i\) 种邮票组合出邮资 \(j\) 所需的最少邮票数。公式如下:
- 解释:
- \(t\):第 \(i\) 种邮票使用的数量。
- \(x_i\):第 \(i\) 种邮票的面值。
- \(j - t \cdot x_i\):剩余邮资,需要前 \(i-1\) 种邮票来补齐。
- \(y_{i-1}(j - t \cdot x_i)\):剩余部分的最少邮票数。
该公式递归地检查在不超过 \(m\) 张邮票的情况下,如何用最少的邮票组合来支付邮资 \(j\)。
1.3.3 上界 \(r_i\) 的计算过程
根据递归函数 \(y_i(j)\),我们定义了 上界 \(r_i\),其计算公式如下:
- 解释:
- 找到最大的 \(j\),使得 \(y_i(j) \leq m\),即 可以使用不超过 \(m\) 张邮票贴出邮资 \(j\)。
- 尝试支付邮资 \(j + 1\) 时,若 \(y_i(j + 1) > m\),即 不能在不超过 \(m\) 张邮票的限制内贴出 \(j + 1\),则 \(j\) 为 断点。
- 我们需要在所有可能的断点中选择 最小的那个 \(j\) 作为 \(r_i\),确保能够覆盖最大连续区间。
1.3.4 分析与逻辑
-
寻找断点:
- 对每个 \(j\),检查是否能在不超过 \(m\) 张邮票的限制下完成支付。
- 一旦 \(j + 1\) 无法满足上述条件,当前 \(j\) 就是上界 \(r_i\)。
-
为何选择最小的 \(j\):
- 在某些邮票设计下,可能会有多个 断点,即多个 \(j\) 满足 \(y_i(j) \leq m\) 且 \(y_i(j + 1) > m\)。我们选择 最小的 \(j\) 作为 \(r_i\),确保得到最长的连续区间。
-
示例分析:
- 若使用面值为 \(1, 3, 5\) 的邮票,且 \(m = 3\),当我们尝试支付 \(j = 9\) 时能够成功,但支付 \(j + 1 = 10\) 时需要超过 3 张邮票,则断点为 \(j = 9\)。此时 \(r_i = 9\)。
1.4 部分搜索树示例与最优解的计算
在这一节,我们通过一个具体的搜索树示例来说明如何根据不同邮票面值计算出连续邮资的上界 \(r_i\),并找到最终的最优解。
1.4.1 示例设置
- 邮票种类:\(n = 4\)
- 每信封最多邮票数:\(m = 3\)
- 初始邮票面值:\(x_1 = 1\)
在此示例中,我们需要设计合理的邮票面值,使得能够贴出的连续邮资区间最大化。每一步都将使用深度优先搜索策略遍历子树,并不断更新可能的解。
1.4.2 搜索树结构与递进计算
-
第一种邮票面值 \(x_1 = 1\):
- 用 \(x_1 = 1\) 的邮票最多贴 3 张,可以覆盖的最大邮资区间为:\[r_1 = 3 \quad \text{(即邮资 1, 2, 3)} \]
- 这意味着,加入更多面值的邮票时,它们的面值必须至少比 \(x_1\) 大 1,即从 2 开始 ,并且最大应该为\(r_1+1=4\) , 因此此节点会分出三个树枝。
- 用 \(x_1 = 1\) 的邮票最多贴 3 张,可以覆盖的最大邮资区间为:
-
第二种邮票面值 \(x_2 = 2\):
- 由深度优先搜索,如果选择 \(x_2 = 2\),则最多 3 张邮票可以覆盖:\[r_2 = 6 \quad \text{(邮资 1 到 6)} \]
- 接下来的邮票面值应至少比 \(x_2\) 多 1,即从 3 开始,且不能超过 \(r_2 + 1 = 7\)。
- 由深度优先搜索,如果选择 \(x_2 = 2\),则最多 3 张邮票可以覆盖:
-
第三种邮票面值 \(x_3 = 3\):
- 若 \(x_3 = 3\),则用 \(x_1, x_2, x_3\) 的组合最多能贴出:\[r_3 = 9 \quad \text{(邮资 1 到 9)} \]
- 第四种邮票的面值应位于 \([4, 10]\) 之间。
- 若 \(x_3 = 3\),则用 \(x_1, x_2, x_3\) 的组合最多能贴出:
-
第四种邮票面值 \(x_4 = 4\):
- 当 \(x_4 = 4\) 时,使用 1、2、3 和 4 这些邮票的组合,可以贴出的最大连续区间为:\[r_4 = 17 \quad \text{(邮资 1 到 17)} \]
- 当 \(x_4 = 4\) 时,使用 1、2、3 和 4 这些邮票的组合,可以贴出的最大连续区间为:
1.4.3 搜索树的遍历与剪枝
在搜索树中,每一层代表一个邮票面值的选择,而每个节点表示当前面值的累计状态。我们采用深度优先遍历来找到最优解。在遍历过程中,如果发现某条路径上的解不优于已有解,则会执行剪枝操作,避免冗余计算。
-
节点 (1, 4, 5, 6):
- 贴出的最大连续区间为 18。
-
节点 (1, 4, 6, 7):
- 通过进一步的搜索,我们找到一个更优的解,其连续区间为:\[\{1, \ldots, 21\} \]
- 通过进一步的搜索,我们找到一个更优的解,其连续区间为:
1.4.4 最优解
- 最优解的邮票面值组合为:\[X = \langle 1, 4, 6, 7 \rangle \]
- 最大连续区间为:\[\{1, \ldots, 21\} \]
这表明,使用面值为 1、4、6、7 的邮票设计,在每封信最多贴 3 张邮票的情况下,可以覆盖的最大连续邮资区间为 1 到 21。
1.5 小结:回溯算法全面解析
(1) 回溯算法的适用范围
回溯算法是一种系统化的搜索策略,广泛应用于组合问题、优化问题以及约束满足问题。具体应用场景包括:
- 八皇后问题:寻找所有不互相攻击的皇后摆放方式。
- 旅行商问题:寻找访问所有城市且路径最短的旅行路径。
- 背包问题:在容量有限的情况下,选择物品使得总价值最大。
- 数独求解和其他复杂的排列组合问题。
(2) 求解条件:多米诺性质
在回溯算法中,多米诺性质要求每一步选择都能够自然衔接后续步骤。每一个解的子结构是后续完整解的一部分。例如,在旅行商问题中,选择一个城市路径后,后续城市的路径构建依赖于当前路径的选择。
(3) 解的表示与构建过程
- 解向量:使用向量来表示问题的解,如 \((x_1, x_2, \ldots, x_i)\) 表示解的部分结构。随着搜索的深入,解的向量会不断扩展,直到形成完整解。
- 通过逐步扩展向量的方式,保证了算法的系统性与逻辑性。
(4) 回溯条件
-
搜索问题的条件:
- 满足约束条件才是可行解,否则立即回溯。
例如,在八皇后问题中,两个皇后不应在同一行、列或对角线。
- 满足约束条件才是可行解,否则立即回溯。
-
优化问题的条件:
- 代价函数:除了约束条件,还需计算解的代价,通过代价函数找到最优解。
例如,在旅行商问题中,代价函数是路径的总长度,目标是使其最小。
- 代价函数:除了约束条件,还需计算解的代价,通过代价函数找到最优解。
- 代价函数裁剪的作用:
若发现某分支中的解代价大于当前最优解,则立即剪枝,停止进一步搜索。
例如,若某路径的部分长度已经大于当前已知的最短路径,则无需继续探索该路径。
(5) 多种分支策略
根据问题性质,可以选择不同的分支策略:
- 深度优先搜索 (DFS):优先深入探索路径的尽头。这适用于需要尽快找到一个可行解的情况。
- 广度优先搜索 (BFS):按层次扩展所有可能解,适用于寻找最优解,但耗费更多空间。
- 启发式搜索:使用特定启发函数来引导搜索。
例如,A*算法在路径规划中使用估计代价来优先访问可能的最优路径。 - 宽深结合策略:交替使用DFS和BFS,在保证效率的同时平衡内存消耗。
(6) 结点状态分类
在搜索树的遍历中,结点状态可分为:
- 白色结点:未被访问的结点。
- 灰色结点:当前正在遍历的结点。
- 黑色结点:已经处理完毕的结点,不再需要访问。
(7) 算法时间复杂度分析
回溯算法的时间复杂度取决于:
- 每个结点的工作量 \(p(n)\):即在某个结点上的计算代价。
- 结点个数 \(f(n)\):搜索树中的总结点数量。
时间复杂度:
- 最坏情况下:时间复杂度通常为指数级,例如 \(O(2^n)\),和蛮力算法类似。
- 平均情况下:由于剪枝策略的应用,实际运行时间要比蛮力算法更好。
(8) 降低时间复杂度的策略
-
优先策略设计:
- 优先选择结点较少的分支或潜在解更多的路径。
- 例如,在背包问题中,可以优先探索价值最高的物品组合。
-
利用对称性剪枝:
- 如果搜索树具有对称结构,可以只探索一部分解。例如,在八皇后问题中,探索一半子树即可推导另一半解。
-
问题分解:
- 将规模为 \(n\) 的问题分解为 \(k\) 个子问题,每个子问题的规模为 \(\frac{n}{k}\)。
- 每个子问题的复杂度为 \(O(2^{n/k})\),总复杂度为:\[T(n) = k \cdot c \cdot 2^{n/k} + O(2^{n/k}) = O(2^{n/k}) = o(2^n) \]
- 这种方法有效降低了整体复杂度,提升了解决问题的效率。
(9) 回溯算法的优势与局限
-
优势:
- 空间复杂度低:回溯算法通常只需维护当前路径及少量状态信息,因此空间开销较小。
- 灵活性高:适用于各种组合问题和约束问题。
-
局限:
- 时间复杂度高:在最坏情况下,仍然是指数时间复杂度。
- 路径依赖性:算法的效率很大程度上依赖于分支策略和剪枝规则的设计。
(10) 小结
回溯算法是一种功能强大的求解策略,广泛用于组合搜索问题和优化问题。其核心思想是通过深度优先搜索和剪枝策略在解空间中高效地探索解集。通过引入代价函数和多种搜索策略,回溯算法能够在复杂度高的问题中找到接近最优的解。
在实践中,通过设计优良的剪枝策略和利用问题的对称性,可以大大提升回溯算法的效率,减少不必要的搜索路径。同时,通过分解问题,可以进一步降低算法的时间复杂度。这些优化方法确保了回溯算法不仅适用于理论研究,更能在实际问题求解中展现出色的性能。