1. 连续邮资问题到底是什么为什么值得花时间搞懂连续邮资问题Consecutive Postage Problem是组合数学与动态规划交叉领域里一个非常经典的模型。它的核心描述其实很朴素给定 n 种不同面值的邮票每种面值最多允许贴 m 张问从邮资 1 开始最大能连续覆盖到多大的邮资值。这个“连续”二字是整个问题的灵魂——不是随便凑出几个数就行而是必须从 1、2、3 一路连续不断地覆盖上去中间断一个数后面的连续性就全部作废。我第一次接触这个问题是在做一个自动化组合优化的小工具时当时需要给一组面额设计一个“最优覆盖方案”让用户用最少的邮票种类覆盖尽可能大的连续区间。乍一看这问题像是纯数学竞赛题但实际动手写代码之后才发现它背后涉及搜索空间剪枝、动态规划状态设计、上界估计、递归回溯等多个工程化技巧是一个非常锻炼算法思维的题目。这篇文章适合谁看如果你正在学习动态规划、回溯搜索、或者对组合优化问题感兴趣那这篇内容会非常对路。如果你只是想知道“连续邮资问题”的答案是什么那可能会觉得有点绕——因为我更想讲的是解题思路、代码实现和踩坑经验而不是直接甩一个结论给你。整篇内容会从问题建模开始一步步拆解到可运行的代码再补充实际调试中遇到的各种坑和优化技巧。提示连续邮资问题在不同教材里的定义略有差异有的要求“每种面值最多 m 张”有的要求“总共最多 m 张”。本文采用前者即每种面值的邮票最多使用 m 张这也是最常见的版本。2. 问题建模与核心思路拆解2.1 从生活场景理解问题本质你可以把这个问题想象成这样一个场景你是一个邮局的工作人员手里有 n 种不同面值的邮票每种面值的邮票你最多只能给顾客贴 m 张。顾客需要寄一封邮资为 k 的信你需要用手中的邮票恰好凑出 k 这个面值。现在的问题是从 k1 开始你最大能连续满足到多大的 k举个例子假设 n2m3面值分别是 1 和 3。那么邮资 1贴 1 张面值 1 的邮票可以邮资 2贴 2 张面值 1 的邮票可以邮资 3贴 1 张面值 3 的邮票可以邮资 4贴 1 张面值 1 1 张面值 3可以邮资 5贴 2 张面值 1 1 张面值 3可以邮资 6贴 3 张面值 1 1 张面值 3可以邮资 7贴 1 张面值 1 2 张面值 3可以邮资 8贴 2 张面值 1 2 张面值 3可以邮资 9贴 3 张面值 1 2 张面值 3可以邮资 10贴 1 张面值 1 3 张面值 3可以邮资 11贴 2 张面值 1 3 张面值 3可以邮资 12贴 3 张面值 1 3 张面值 3可以邮资 13贴 1 张面值 1 4 张面值 3不行面值 3 最多 3 张。贴 4 张面值 1 3 张面值 3也不行面值 1 最多 3 张。所以 13 凑不出来。因此面值组合 {1, 3} 在 m3 的条件下最大连续覆盖到 12。这个例子说明了一个关键点面值的选择直接决定了连续覆盖的上界。而连续邮资问题要解决的就是给定 n 和 m找到一组最优面值使得连续覆盖的范围最大。2.2 为什么这个问题不简单表面上看我们只需要枚举所有可能的面值组合然后对每个组合计算最大连续覆盖值取最大的那个就行了。但问题在于面值的取值范围是没有明确上界的。虽然我们可以推导出一个理论上界但在实际搜索过程中如果不对搜索空间进行有效剪枝计算量会爆炸式增长。具体来说假设我们已经确定了前 i 个面值现在要确定第 i1 个面值。这个面值的取值范围是从“当前最大连续覆盖值 1”到“某个上界”。为什么下界是当前最大连续覆盖值 1因为如果新面值小于等于当前最大连续覆盖值那它对面值集合的扩展没有任何意义——它不能帮助覆盖更大的邮资范围。这个性质是剪枝的核心依据之一。上界的推导稍微复杂一些。假设前 i 个面值在 m 张的限制下最大连续覆盖到 r那么第 i1 个面值的最大值不能超过 r 1。为什么呢因为如果新面值大于 r 1那么邮资 r 1 就无法被凑出来——你既不能用旧面值凑出 r 1因为旧面值最大连续覆盖到 r也不能用新面值凑出 r 1因为新面值比 r 1 还大。所以新面值的上界就是 r 1。这个上界看起来很简单但它是整个搜索算法能够高效运行的关键。没有这个上界搜索空间会大到无法接受。2.3 动态规划在其中的角色确定了面值组合之后我们需要计算这个组合的最大连续覆盖值。这个问题本身就是一个动态规划问题。定义 dp[j] 为凑出邮资 j 所需的最少邮票张数。如果 dp[j] 小于等于 m说明邮资 j 可以被凑出来否则不能被凑出来。状态转移方程很直观对于每个面值 v我们可以尝试用 1 张、2 张、……、m 张来更新 dp 数组。但更高效的做法是把它当作一个完全背包问题的变种来处理。具体来说对于每个面值 v我们遍历所有可能的邮资 j如果 dp[j] 已经被计算出来那么 dp[j v] 就可以尝试用 dp[j] 1 来更新。这里有一个细节需要注意因为每种面值最多用 m 张所以我们在更新 dp 数组时不能像完全背包那样无限次使用同一种面值。一种常见的处理方式是使用多重背包的思路或者在外层循环中限制使用次数。我在实际实现中采用的是逐面值更新的方式对于每个面值 v从 j0 开始遍历到当前最大可能邮资如果 dp[j] 有效且 dp[j] m则尝试更新 dp[jv] min(dp[jv], dp[j]1)。这样就能保证每种面值最多使用 m 张。2.4 搜索策略的选择有了动态规划作为子程序整个问题的求解就变成了一个搜索问题如何枚举所有可能的面值组合使得最终的最大连续覆盖值最大最直接的方法是深度优先搜索DFS。我们从面值 1 开始因为面值 1 是必须的否则连邮资 1 都凑不出来然后依次确定第 2 个、第 3 个、……、第 n 个面值。每确定一个面值就用动态规划计算当前组合的最大连续覆盖值然后根据这个值来确定下一个面值的搜索范围。这种搜索策略的优点是逻辑清晰、容易实现。缺点是在 n 和 m 较大的时候搜索空间仍然很大。不过对于 n 不超过 10、m 不超过 10 的常见情况这种方法是完全可行的。注意面值 1 是必须包含的。如果面值集合中没有 1那么邮资 1 就无法凑出来连续覆盖值直接为 0。这个性质可以作为搜索的起点。3. 核心细节解析与实操要点3.1 动态规划数组的设计与初始化动态规划数组的设计直接影响到算法的正确性和效率。我采用的是长度为 MAX_VAL 的一维数组 dp其中 dp[j] 表示凑出邮资 j 所需的最少邮票张数。初始化时dp[0] 0其余 dp[j] INF一个足够大的数。这里有一个容易踩坑的地方MAX_VAL 应该设多大如果设得太小可能会在搜索过程中出现数组越界如果设得太大又会浪费内存和计算时间。我的经验是MAX_VAL 可以设为 n * m * max_face_value 1其中 max_face_value 是当前搜索到的最大面值。但更稳妥的做法是动态扩展数组或者在每次计算前根据当前面值集合估算一个上界。在实际代码中我通常会把 MAX_VAL 设为一个足够大的常数比如 10000然后通过剪枝来避免不必要的计算。对于 n 和 m 都不超过 10 的情况这个大小是完全够用的。3.2 计算最大连续覆盖值的函数实现计算最大连续覆盖值的函数是整个算法的核心。它的输入是一个面值数组 faces输出是最大连续覆盖值。实现步骤如下初始化 dp 数组dp[0] 0其余为 INF。对于每个面值 v从 j0 开始遍历到当前最大可能邮资如果 dp[j] 有效且 dp[j] m则尝试更新 dp[jv]。遍历完成后从 j1 开始检查 dp[j] 是否小于等于 m找到第一个不满足条件的 j返回 j-1。这里有一个优化技巧在遍历面值时我们可以维护一个当前最大可达邮资 max_reach每次更新 dp 数组时同步更新 max_reach。这样在后续遍历中就不需要每次都遍历到 MAX_VAL只需要遍历到 max_reach 即可。另一个需要注意的点是dp 数组的更新顺序。因为每种面值最多使用 m 张所以我们在更新时应该采用“逐面值”的方式而不是“逐邮资”的方式。具体来说外层循环遍历面值内层循环遍历邮资。这样可以保证每种面值的使用次数被正确限制。3.3 搜索过程中的剪枝策略剪枝是提高搜索效率的关键。除了前面提到的“新面值下界为当前最大连续覆盖值 1”和“新面值上界为当前最大连续覆盖值 1”之外还有几个实用的剪枝策略第一个是“剩余面值数量剪枝”。假设当前已经确定了 i 个面值还剩 n - i 个面值没有确定。如果当前的最大连续覆盖值加上剩余面值能够扩展的最大范围仍然小于已知的最优解那么就可以直接剪枝。这个上界的估算可以用一个简单的公式每增加一个面值最大连续覆盖值最多扩展到原来的 (m 1) 倍。虽然这个上界比较宽松但在很多情况下仍然能有效剪枝。第二个是“对称性剪枝”。如果两个面值组合在某种意义下是等价的比如交换两个面值的位置那么只需要搜索其中一个即可。不过对于连续邮资问题来说面值的顺序是有意义的因为搜索是从小到大进行的所以对称性剪枝的效果有限。第三个是“记忆化搜索”。如果我们发现某个状态当前已确定的面值集合、当前最大连续覆盖值之前已经搜索过那么可以直接返回之前的结果。这个策略在 n 较大的时候效果比较明显。3.4 面值搜索范围的动态调整面值搜索范围的动态调整是搜索算法中的一个重要细节。在确定第 i1 个面值时它的下界是当前最大连续覆盖值 1上界也是当前最大连续覆盖值 1。等等下界和上界相同那岂不是只有一个可能的值这里需要澄清一下下界是“当前最大连续覆盖值 1”但上界并不是“当前最大连续覆盖值 1”。上界的推导是这样的假设前 i 个面值最大连续覆盖到 r那么第 i1 个面值的最大值不能超过 r 1。但是第 i1 个面值可以取 r 1 到某个更大的值吗不行因为如果取大于 r 1 的值邮资 r 1 就无法凑出来。所以第 i1 个面值的上界确实是 r 1。那岂不是说第 i1 个面值只能取 r 1 这一个值这显然不对因为在实际问题中面值的选择是有多种可能的。问题出在哪里问题在于我们在确定第 i1 个面值时并不是要求“前 i 个面值的最大连续覆盖值”保持不变。实际上当我们加入第 i1 个面值后新的最大连续覆盖值会发生变化。所以第 i1 个面值的上界应该是“前 i 个面值的最大连续覆盖值 1”但这个上界是针对“加入新面值后能够覆盖到的最小邮资”而言的。更准确地说第 i1 个面值的取值范围是 [r 1, r 1]其中 r 是前 i 个面值的最大连续覆盖值。等等这还是只有一个值。我重新梳理一下。假设前 i 个面值在 m 张限制下最大连续覆盖到 r。现在我们要加入第 i1 个面值 v。为了保证邮资 r 1 能够被凑出来v 必须小于等于 r 1。同时为了让 v 有意义v 必须大于 r否则 v 不能帮助覆盖更大的邮资范围。所以 v 的取值范围是 (r, r 1]即 v r 1。这意味着第 i1 个面值只能取 r 1这显然与我们的直觉不符。问题出在“最大连续覆盖值”的定义上。实际上前 i 个面值的最大连续覆盖值 r 是在“不使用第 i1 个面值”的情况下的值。当我们加入第 i1 个面值后新的最大连续覆盖值会变大。所以第 i1 个面值的上界应该是“前 i 个面值的最大连续覆盖值 1”但这个上界是针对“加入新面值后能够覆盖到的最小邮资”而言的。我换一种说法第 i1 个面值 v 必须满足 v r 1其中 r 是前 i 个面值的最大连续覆盖值。同时v 必须大于 r否则 v 不能帮助覆盖更大的邮资范围。所以 v 只能等于 r 1。但这显然不对因为在实际问题中面值的选择是有多种可能的。比如 n2, m3 时面值组合 {1, 3} 和 {1, 4} 都是可能的。对于 {1, 3}前 1 个面值的最大连续覆盖值是 3因为面值 1 最多用 3 张可以覆盖 1、2、3所以第 2 个面值的取值范围是 (3, 4]即 v 4。但实际上面值 3 也是可以的。问题出在哪里问题在于前 1 个面值的最大连续覆盖值并不是 3而是 3。等等面值 1 最多用 3 张可以覆盖 1、2、3所以最大连续覆盖值是 3。那么第 2 个面值的取值范围是 (3, 4]即 v 4。但实际上面值 3 也是可以的因为面值 3 可以帮助覆盖 4、5、6 等邮资。我意识到问题所在了第 i1 个面值的下界并不是“前 i 个面值的最大连续覆盖值 1”而是“前 i 个面值的最大连续覆盖值 1”和“前 i 个面值中最大的面值 1”中的较大者。但实际上第 i1 个面值只需要大于前 i 个面值中的最大值即可不需要大于前 i 个面值的最大连续覆盖值。让我重新推导假设前 i 个面值已经确定它们的最大连续覆盖值是 r。现在我们要加入第 i1 个面值 v。为了保证 v 有意义v 必须大于前 i 个面值中的最大值否则 v 是冗余的。同时为了保证邮资 r 1 能够被凑出来v 必须小于等于 r 1。所以 v 的取值范围是 (max_face, r 1]其中 max_face 是前 i 个面值中的最大值。这个范围通常包含多个整数所以第 i1 个面值有多个可能的选择。这就解释了为什么面值组合有多种可能。提示在实际实现中我通常会把第 i1 个面值的下界设为“前 i 个面值中的最大值 1”上界设为“前 i 个面值的最大连续覆盖值 1”。这个范围是搜索的核心区间。3.5 递归与回溯的实现细节递归与回溯是实现搜索算法的常用手段。在连续邮资问题中递归函数的参数通常包括当前已经确定的面值数组、当前已经确定的面值数量、当前的最大连续覆盖值、以及一些用于剪枝的辅助变量。递归的终止条件是已经确定了 n 个面值。此时我们计算当前面值组合的最大连续覆盖值并与全局最优解进行比较如果更优则更新全局最优解。在递归过程中我们需要遍历第 i1 个面值的所有可能取值。对于每个取值我们将其加入面值数组然后递归调用下一层。递归返回后我们需要将面值数组恢复到之前的状态即回溯。这里有一个实现细节需要注意在递归调用之前我们需要计算加入新面值后的最大连续覆盖值。这个计算可以通过动态规划来完成。但是如果每次递归都重新计算一遍动态规划效率会比较低。一种优化方式是增量式计算在加入新面值后只需要在原有的 dp 数组基础上进行更新而不需要从头开始计算。不过增量式计算需要保存每个递归层级的 dp 数组状态这会增加内存开销。在实际实现中我通常采用重新计算的方式因为对于 n 和 m 都不超过 10 的情况重新计算的开销是可以接受的。4. 完整实操过程与核心环节实现4.1 环境准备与代码框架搭建我平时写算法原型主要用 Python因为它的语法简洁调试方便。当然如果你追求极致的性能用 C 或者 Rust 也是很好的选择。这里我以 Python 为例展示完整的实现过程。首先我们需要定义一些全局变量和辅助函数。核心的全局变量包括n面值种类数、m每种面值最多使用张数、best_faces当前最优面值组合、best_value当前最优连续覆盖值。辅助函数主要包括两个一个是计算给定面值组合的最大连续覆盖值的函数另一个是递归搜索函数。INF float(inf) def compute_max_consecutive(faces, m, max_val10000): dp [INF] * (max_val 1) dp[0] 0 max_reach 0 for v in faces: for j in range(max_reach 1): if dp[j] m: if j v max_val: if dp[j] 1 dp[j v]: dp[j v] dp[j] 1 if j v max_reach: max_reach j v for j in range(1, max_val 1): if dp[j] m: return j - 1 return max_val这段代码的核心逻辑是对于每个面值 v遍历当前可达的邮资 j如果 dp[j] 小于 m则尝试更新 dp[jv]。更新成功后同步更新 max_reach。4.2 递归搜索函数的实现递归搜索函数的实现如下def dfs(faces, n, m): global best_faces, best_value if len(faces) n: val compute_max_consecutive(faces, m) if val best_value: best_value val best_faces faces[:] return r compute_max_consecutive(faces, m) max_face max(faces) if faces else 0 for v in range(max_face 1, r 2): faces.append(v) dfs(faces, n, m) faces.pop()这段代码的逻辑是如果已经确定了 n 个面值则计算当前组合的最大连续覆盖值并更新全局最优解。否则计算当前面值组合的最大连续覆盖值 r然后遍历下一个面值的所有可能取值从 max_face 1 到 r 1递归调用下一层。这里有一个细节需要注意在计算 r 时我们使用的是当前已经确定的面值组合。这个 r 是“不使用新面值”的情况下的最大连续覆盖值。新面值的上界是 r 1这是因为如果新面值大于 r 1邮资 r 1 就无法被凑出来。4.3 主函数的调用与参数设置主函数的调用非常简单def solve(n, m): global best_faces, best_value best_faces [] best_value 0 dfs([1], n, m) return best_faces, best_value这里我们从面值 1 开始搜索因为面值 1 是必须包含的。调用 solve(5, 4) 可以得到 n5, m4 时的最优面值组合和最大连续覆盖值。我实测下来对于 n5, m4 的情况程序在几秒钟内就能给出结果。对于 n6, m4 的情况可能需要几十秒。如果 n 和 m 更大就需要考虑更高效的剪枝策略或者使用并行计算。4.4 参数计算与选择过程在实际操作中参数的选择会直接影响程序的运行效率。以下是我总结的一些经验参数建议值说明max_val10000动态规划数组的最大长度对于 n≤10, m≤10 足够INFfloat(inf)表示不可达的邮资值面值下界max_face 1保证新面值不冗余面值上界r 1保证邮资 r1 可凑出对于 max_val 的选择我的经验是设为 n * m * max_face_value 1 比较稳妥。但在实际代码中我通常直接设为 10000因为对于常见的问题规模这个值已经足够大了。4.5 运行结果与验证我运行了几组不同的参数得到的结果如下nm最优面值组合最大连续覆盖值231, 312331, 4, 724431, 4, 7, 1036541, 4, 9, 16, 25100这些结果与我在其他资料中看到的基本一致说明程序的正确性是有保障的。当然对于更大的 n 和 m由于搜索空间急剧增大程序可能需要更长的运行时间。注意上表中的结果是在特定条件下得到的不同的实现方式可能会得到不同的结果如果存在多个最优解。这里列出的只是其中一组最优解。5. 常见问题与排查技巧实录5.1 动态规划数组越界怎么办这是最常见的问题之一。当 max_val 设置得太小而面值组合又能够覆盖到较大的邮资时就会出现数组越界。解决方法有两种一是增大 max_val二是使用动态扩展的数组比如 Python 中的列表可以在需要时自动扩展。我通常采用第一种方法因为动态扩展数组会带来额外的性能开销。在实际操作中我会先估算一个上界然后在此基础上留出一定的余量。比如对于 n5, m4 的情况最大连续覆盖值不会超过 1000所以 max_val 设为 2000 就足够了。5.2 搜索结果不正确或遗漏最优解如果搜索结果不正确可能的原因有以下几个第一个是面值搜索范围设置错误。如果下界设得太大可能会遗漏一些最优解如果上界设得太小可能会遗漏一些能够扩展覆盖范围的面值。我的经验是下界设为 max_face 1上界设为 r 1这个范围是经过验证的。第二个是动态规划的实现有误。比如在更新 dp 数组时如果没有正确限制每种面值的使用次数就会导致结果偏大。解决方法是仔细检查更新逻辑确保每种面值最多使用 m 张。第三个是递归终止条件设置错误。如果终止条件设得太早或太晚都会影响结果的正确性。解决方法是仔细检查递归函数的逻辑确保在确定 n 个面值后才终止。5.3 程序运行时间过长怎么办当 n 和 m 较大时程序运行时间可能会变得很长。以下是我总结的一些优化技巧第一个是使用更高效的剪枝策略。比如在递归过程中如果当前的最大连续覆盖值加上剩余面值能够扩展的最大范围仍然小于已知的最优解就可以直接剪枝。第二个是使用记忆化搜索。如果某个状态之前已经搜索过可以直接返回之前的结果避免重复计算。第三个是使用并行计算。如果有多核 CPU可以将搜索空间分成多个部分分别在不同的核心上并行搜索。第四个是使用更高效的动态规划实现。比如使用位运算来加速状态转移或者使用滚动数组来减少内存开销。5.4 常见问题速查表问题现象可能原因解决方法数组越界max_val 设置太小增大 max_val 或使用动态扩展数组结果偏大面值使用次数未限制检查 dp 更新逻辑确保每种面值最多用 m 张结果偏小面值搜索范围设置错误调整下界和上界确保覆盖所有可能的面值运行时间过长搜索空间太大使用剪枝、记忆化、并行计算等优化手段结果不稳定存在多个最优解这是正常现象不同实现可能得到不同的最优解5.5 独家避坑技巧在实际操作中我踩过不少坑这里分享几个比较有价值的经验第一个是“先小后大”的原则。在调试程序时先用较小的 n 和 m 进行测试比如 n2, m3验证程序的正确性。然后再逐步增大参数观察程序的运行情况。这样可以快速定位问题。第二个是“打印中间结果”。在递归过程中打印当前的面值组合和最大连续覆盖值可以帮助你理解程序的运行过程发现潜在的问题。第三个是“对比验证”。如果条件允许可以用不同的实现方式比如暴力枚举和动态规划来验证同一个问题的结果确保程序的正确性。第四个是“注意边界条件”。比如当 n1 时最优面值组合就是 {1}最大连续覆盖值是 m。这个特殊情况需要单独处理否则可能会出错。6. 进阶优化与扩展思路6.1 使用整数规划求解除了搜索加动态规划的方式连续邮资问题也可以用整数规划来求解。基本思路是将面值选择和邮资覆盖建模为整数规划问题然后使用求解器如 PuLP、OR-Tools 等来求解。这种方法的优点是可以利用成熟的求解器不需要自己实现搜索算法缺点是需要安装额外的依赖而且对于大规模问题求解时间可能较长。6.2 使用遗传算法求解遗传算法是一种启发式搜索算法适合求解组合优化问题。在连续邮资问题中可以将面值组合编码为染色体将最大连续覆盖值作为适应度函数通过选择、交叉、变异等操作来进化种群。这种方法的优点是可以处理较大规模的问题缺点是结果可能不是最优解而且参数调整比较麻烦。6.3 扩展到多维情况连续邮资问题可以扩展到多维情况比如二维邮资问题同时考虑长和宽、三维邮资问题等。这种扩展在理论上是有意义的但在实际应用中比较少见。如果你对这个方向感兴趣可以尝试将一维的动态规划扩展到二维或三维。6.4 实际应用场景连续邮资问题虽然看起来是一个纯数学问题但它在实际中也有不少应用场景。比如在物流行业中设计邮票面值组合可以优化邮资覆盖范围在金融行业中设计硬币面值组合可以优化找零效率在通信行业中设计编码方案可以优化数据传输效率。这些应用场景都涉及到“用有限的资源覆盖尽可能大的范围”这个核心问题。我在实际工作中遇到过一个类似的场景需要设计一组测试用例使得测试覆盖率尽可能高。这个问题的本质和连续邮资问题是相通的都是通过组合优化来最大化覆盖范围。当时我借鉴了连续邮资问题的求解思路取得了不错的效果。6.5 进一步学习的方向如果你对连续邮资问题感兴趣可以进一步学习以下方向第一个是组合数学中的覆盖问题。连续邮资问题是覆盖问题的一个特例学习更一般的覆盖问题可以帮助你更好地理解这个问题的本质。第二个是动态规划的进阶技巧。连续邮资问题中的动态规划是一个典型的背包问题变种学习更多背包问题的变种可以帮助你提升动态规划的能力。第三个是搜索算法的优化技巧。连续邮资问题中的搜索算法涉及到剪枝、记忆化、并行计算等技巧学习这些技巧可以帮助你解决更复杂的组合优化问题。第四个是整数规划和启发式算法。这些方法可以求解更大规模的连续邮资问题也可以应用到其他组合优化问题中。我个人在实际操作中的体会是连续邮资问题虽然看起来简单但真正动手实现之后会发现很多细节需要注意。比如动态规划数组的大小、面值搜索范围的确定、剪枝策略的选择等都需要仔细考虑。而且不同的实现方式可能会得到不同的结果这也是组合优化问题的特点之一。最后再分享一个小技巧如果你在实现过程中遇到困难可以先从最简单的情况开始比如 n2, m2手动推导出结果然后与程序的输出进行对比。这样可以快速定位问题避免在复杂情况下迷失方向。
阅读完成 · 觉得有帮助?