ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

贪心算法实战:构造数字和最小的正整数(附Python代码)

贪心算法实战:构造数字和最小的正整数(附Python代码) 小红最近在准备编程竞赛遇到了一道关于正整数构造的题目。这类题目看似简单却暗藏玄机——很多选手在时间限制内无法找到最优解或者写出了冗长复杂的代码。今天我们就来深入剖析这道2026年7月10日的每日一题看看如何用简洁高效的思路解决它。这道题的核心在于给定一个目标数值要求构造一个正整数使得该数的各位数字之和等于目标值并且这个数要尽可能小。这听起来像是一道小学数学题但在编程竞赛中它考察的是选手对数字构造、贪心算法和边界情况处理的能力。1. 问题重述与难点分析题目要求给定一个正整数s构造一个正整数n使得n的各位数字之和等于s并且n的值尽可能小。关键难点在于数字的位数越少数值越小吗不一定——100比99大但位数相同如何平衡位数少和高位数字小的矛盾特殊情况处理s1时应该输出什么s0是否合法常见错误思路直接构造s个1当s10时得到1111111111但正确答案是19从9开始减可能得到不最小的排列忽略前导零的问题构造的数字必须是合法的正整数2. 贪心算法原理与数学证明2.1 贪心策略的正确性证明要让数值最小我们需要位数尽可能少用更大的数字填充低位高位尽可能小从个位开始分配较大的数字数学证明 假设有两个数ABCD和ABCD如果A A那么无论后面几位如何ABCD一定小于ABCD。因此我们应该让最高位尽可能小。但是当位数不同时比如9和10虽然19但109。所以要先确定最小位数再让高位最小。2.2 最小位数的计算最小位数 ⌈s/9⌉向上取整s10需要2位10÷9≈1.11→2s18需要2位18÷92s19需要3位19÷9≈2.11→33. 完整算法实现步骤3.1 算法流程分解def construct_min_number(s): 构造各位数字之和为s的最小正整数 :param s: 目标数字和 :return: 构造的最小正整数 if s 0: return 0 # 根据题目要求有些比赛允许s0时返回0 # 计算最少需要的位数 num_digits (s 8) // 9 # 等价于向上取整(s/9) # 初始化结果数组每一位先设为0 digits [0] * num_digits remaining s # 从个位最后一位开始分配数字 for i in range(num_digits-1, -1, -1): # 当前位能分配的最大数字但要保证剩余数字能分配完 current_max min(9, remaining) # 如果是最高位至少要分配1不能有前导零 if i 0: current_max min(9, max(1, remaining)) digits[i] current_max remaining - current_max # 将数字数组转换为整数 result 0 for digit in digits: result result * 10 digit return result3.2 逐步演算示例以s10为例计算位数(108)//9 18//9 2位初始化[0, 0]从个位开始个位索引1min(9,10)9剩余1十位索引0min(9,max(1,1))1剩余0得到数字19以s28为例位数(288)//9 36//9 4位分配过程个位9剩余19十位9剩余10百位9剩余1千位1剩余0结果19994. 边界情况与特殊处理4.1 s0的情况def handle_zero_case(s): if s 0: # 根据题目要求可能返回0或1 # 通常竞赛中s0时要求返回0 return 0 return construct_min_number(s)4.2 s1到s9的情况这些情况比较简单直接返回s本身即可s1 → 1s2 → 2...s9 → 94.3 大数测试# 测试一些边界值 test_cases [0, 1, 9, 10, 18, 19, 28, 100, 1000] for s in test_cases: result construct_min_number(s) digit_sum sum(int(d) for d in str(result)) print(fs{s:4d} → 结果{result:15d} → 验证和{digit_sum})5. 竞赛中的优化技巧5.1 数学公式直接计算我们可以直接通过数学计算得到结果避免数组操作def construct_min_number_optimized(s): if s 0: return 0 num_digits (s 8) // 9 result 0 # 最高位特殊处理 first_digit s - 9 * (num_digits - 1) result first_digit # 后面全是9 for _ in range(num_digits - 1): result result * 10 9 return result5.2 字符串构造法更简洁def construct_min_number_string(s): if s 0: return 0 num_digits (s 8) // 9 first_digit s - 9 * (num_digits - 1) # 构造字符串最高位 (位数-1)个9 result_str str(first_digit) 9 * (num_digits - 1) return int(result_str)6. 常见错误与调试方法6.1 错误类型分析错误类型错误代码示例正确解法前导零错误直接构造0开头的数字最高位至少为1位数计算错误使用向下取整而不是向上取整(s8)//9分配顺序错误从高位开始分配小数字从低位开始分配大数字6.2 调试技巧def debug_construction(s): print(f 调试 s{s} ) num_digits (s 8) // 9 print(f需要位数: {num_digits}) digits [] remaining s for i in range(num_digits-1, -1, -1): current_max min(9, remaining) if i 0: # 最高位 current_max max(1, current_max) digits.insert(0, current_max) # 插入到前面 remaining - current_max print(f第{i}位分配: {current_max}, 剩余: {remaining}) result int(.join(map(str, digits))) print(f最终结果: {result}) return result # 测试调试功能 debug_construction(28)7. 算法复杂度分析7.1 时间复杂度位数计算O(1)数字分配O(log s) 因为位数与log s成正比总体复杂度O(log s)7.2 空间复杂度存储数字数组O(log s)优化版本O(1)直接计算7.3 大数处理能力该算法可以处理非常大的s值比如s10^18因为位数最多约s/9在现代计算机可接受范围内不需要复杂的数学运算8. 变种问题与扩展思考8.1 变种1构造最大数如果要构造各位数字之和为s的最大数策略正好相反位数尽可能多用1填充但题目通常要求位数固定def construct_max_number(s, num_digits): 构造指定位数、数字和为s的最大数 if s 9 * num_digits or s num_digits: return -1 # 不可能 result [0] * num_digits remaining s # 从高位开始分配大数字 for i in range(num_digits): current_max min(9, remaining) result[i] current_max remaining - current_max return int(.join(map(str, result)))8.2 变种2包含特定约束比如要求数字中不能有0或者必须包含某些数字等。这类问题需要结合回溯算法。8.3 实际应用场景这种数字构造算法在密码学中的数字分解游戏开发中的数值平衡数据压缩中的数字编码都有实际应用价值。9. 竞赛实战建议9.1 编码模板准备一个通用的数字构造模板class DigitConstructor: staticmethod def min_number_with_digit_sum(s): 构造数字和为s的最小数 if s 0: return 0 num_digits (s 8) // 9 first_digit s - 9 * (num_digits - 1) return int(str(first_digit) 9 * (num_digits - 1)) staticmethod def max_number_with_digit_sum(s, num_digits): 构造指定位数、数字和为s的最大数 if s 9 * num_digits or s num_digits: return -1 result [] for i in range(num_digits): digit min(9, s) result.append(str(digit)) s - digit return int(.join(result))9.2 测试用例设计一定要测试的边界情况s0, 1, 9, 10, 18, 19s9的倍数s9的倍数1大数测试s1000, 100009.3 性能优化在竞赛中如果s很大但只需要输出数字的位数直接返回(s8)//9就是最小位数这道题的核心在于理解数字构造的本质特征掌握贪心算法的应用场景。通过从低位到高位分配最大可能数字的策略我们能够高效地解决这类问题。在实际编程竞赛中这类题目通常作为热身题出现但如果没有掌握正确的思路很容易陷入复杂的逻辑判断中。建议读者亲自实现代码并尝试解决相关的变种问题这样才能在竞赛中快速识别问题类型并给出最优解。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进