
在日常刷题过程中我们经常会遇到一些看似简单但需要巧妙思维的题目。今天这道“小红的正整数构造”题就很好地考察了对数字性质的理解和构造能力。无论你是刚开始接触算法的新手还是有一定基础的开发者掌握这类题目的解法都能提升你的编程思维和代码实现能力。1. 题目背景与需求分析1.1 题目描述小红需要构造一个正整数满足以下条件这个正整数恰好有 k 个正整数因子包括 1 和它本身在所有满足条件的正整数中这个数要尽可能小例如当 k4 时满足条件的数有 6因子1,2,3,6、8因子1,2,4,8等其中最小的就是 6。1.2 问题本质理解这道题的核心在于理解正整数的因子个数与质因数分解之间的关系。根据数论知识任何一个大于1的正整数都可以唯一分解为质因数的乘积n p₁^a₁ × p₂^a₂ × ... × pₘ^aₘ那么 n 的因子个数为(a₁1) × (a₂1) × ... × (aₘ1)因此题目转化为找到一组指数 a₁, a₂, ..., aₘ使得它们的乘积加1等于 k并且对应的 n 值最小。2. 数学原理与算法思路2.1 因子个数定理深入理解因子个数定理是解决本题的关键。让我们通过几个例子来加深理解质数 p因子为 1 和 p因子个数为 2对应分解为 p¹指数为 1因子个数 11 2完全平方数 36 2² × 3²因子个数 (21)×(21) 912 2² × 3¹因子个数 (21)×(11) 62.2 最小化策略要使构造的数最小我们需要使用尽可能小的质数2, 3, 5, 7, ...将较大的指数分配给较小的质数指数应该从大到小排列因为小质数的指数影响更大例如对于 k12 的分解12 3×2×2对应指数 2,1,1 → 数 2² × 3¹ × 5¹ 6012 4×3对应指数 3,2 → 数 2³ × 3² 72显然 60 72所以第一种分解更优3. 算法设计与实现3.1 深度优先搜索解法我们可以使用深度优先搜索来枚举所有可能的指数组合找到对应的最小数。import math from functools import lru_cache class Solution: def smallestNumberWithKFactors(self, k: int) - int: if k 1: return 1 # 前16个质数足够处理大多数情况 primes [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53] # 记忆化搜索参数当前质数索引剩余因子数当前数值 lru_cache(None) def dfs(idx, remaining, current): if remaining 1: return current if idx len(primes): return float(inf) res float(inf) prime primes[idx] # 尝试当前质数的各种指数 exponent 1 temp current while exponent * exponent remaining: if remaining % (exponent 1) 0: next_remaining remaining // (exponent 1) # 注意防止数值溢出 if temp 10**18 // (prime ** exponent): break next_current temp * (prime ** exponent) res min(res, dfs(idx 1, next_remaining, next_current)) exponent 1 # 不选当前质数 res min(res, dfs(idx 1, remaining, current)) return res result dfs(0, k, 1) return result if result ! float(inf) else -13.2 动态规划解法对于较大的 k 值我们可以使用动态规划来优化def smallestNumberWithKFactorsDP(k: int) - int: if k 1: return 1 # 质数列表 primes [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53] n len(primes) # dp[i][j] 表示使用前i个质数构造因子个数为j的最小数字 # 使用字典来避免存储过大数组 dp [{} for _ in range(n 1)] dp[0][1] 1 for i in range(1, n 1): prime primes[i - 1] # 复制上一状态 for factors, value in dp[i - 1].items(): dp[i][factors] min(dp[i].get(factors, float(inf)), value) # 尝试当前质数的各种指数 for factors, value in dp[i - 1].items(): exponent 1 current_value value while True: current_value * prime if current_value 10**18: # 防止溢出 break new_factors factors * (exponent 1) if new_factors k * 2: # 适当限制范围 break if new_factors not in dp[i] or current_value dp[i][new_factors]: dp[i][new_factors] current_value exponent 1 # 在所有状态中寻找因子个数为k的最小值 result float(inf) for i in range(n 1): if k in dp[i]: result min(result, dp[i][k]) return result if result ! float(inf) else -14. 完整测试用例与验证4.1 测试用例设计为了验证算法的正确性我们需要设计全面的测试用例def test_solution(): test_cases [ (1, 1), # 边界情况1个因子 (2, 2), # 质数情况 (3, 4), # 平方数情况4的因子为1,2,4 (4, 6), # 题目示例 (6, 12), # 复合情况 (8, 24), # 2^3 * 3 (12, 60), # 2^2 * 3 * 5 (16, 120), # 2^3 * 3 * 5 ] solution Solution() for k, expected in test_cases: result solution.smallestNumberWithKFactors(k) print(fk{k}: 期望{expected}, 实际{result}, {通过 if result expected else 失败}) # 验证因子个数是否正确 if result ! -1: factors count_factors(result) print(f 验证: {result}的因子个数为{factors}, {正确 if factors k else 错误}) def count_factors(n: int) - int: 计算正整数n的因子个数 if n 1: return 1 count 0 i 1 while i * i n: if n % i 0: count 1 if i ! n // i: count 1 i 1 return count # 运行测试 test_solution()4.2 性能测试对于较大的 k 值我们需要测试算法的性能import time def performance_test(): k_values [10, 50, 100, 200, 500] solution Solution() for k in k_values: start_time time.time() result solution.smallestNumberWithKFactors(k) end_time time.time() print(fk{k}: 结果{result}, 耗时{end_time - start_time:.4f}秒) if result ! -1: factors count_factors(result) print(f 验证: 因子个数{factors}) performance_test()5. 常见问题与解决方案5.1 数值溢出问题在计算过程中数值可能非常大容易溢出解决方案def safe_multiply(a: int, b: int, limit: int 10**18) - int: 安全乘法防止溢出 if a limit // b: return limit 1 # 表示溢出 return a * b5.2 质数选择策略使用过多或过少的质数都会影响算法效率优化策略def get_optimal_primes(k: int) - list: 根据k值动态选择质数数量 # 估算需要的质数个数log2(k) * 2 通常足够 import math prime_count min(20, max(10, int(math.log2(k) * 2 1))) # 生成前prime_count个质数 primes [] num 2 while len(primes) prime_count: if all(num % p ! 0 for p in primes): primes.append(num) num 1 return primes5.3 搜索剪枝优化深度优先搜索需要合理的剪枝策略def optimized_dfs(idx, remaining, current, best, primes, k): # 剪枝1当前值已经超过已知最优解 if current best: return best # 剪枝2剩余因子数无法继续分解 if remaining 1: return min(best, current) # 剪枝3质数用尽但剩余因子数不为1 if idx len(primes): return best prime primes[idx] # 剪枝4估算最小可能值 min_possible current * (prime ** (count_min_exponents(remaining) - 1)) if min_possible best: return best # ... 继续搜索6. 算法优化与进阶技巧6.1 记忆化搜索的优化使用更高效的数据结构来存储中间结果from collections import defaultdict import math class OptimizedSolution: def __init__(self): self.memo {} self.primes self.generate_primes(50) # 预生成更多质数 def generate_primes(self, n: int) - list: 生成前n个质数 primes [] is_prime [True] * (n * 20) # 足够大的范围 is_prime[0] is_prime[1] False for i in range(2, len(is_prime)): if is_prime[i]: primes.append(i) if len(primes) n: break for j in range(i*i, len(is_prime), i): is_prime[j] False return primes def solve(self, k: int) - int: if k 1: return 1 # 对k进行质因数分解获取指数组合 factors self.factorize_k(k) return self.find_min_number(factors) def factorize_k(self, k: int) - list: 将k-1分解为指数形式降序排列 # k (a11)(a21)...(am1) # 我们需要找到指数组合a1, a2, ..., am降序 result [] temp k # 从大到小尝试分解 while temp 1: found False for divisor in range(int(math.sqrt(temp)), 1, -1): if temp % divisor 0: result.append(divisor - 1) temp // divisor found True break if not found: # 质数情况 result.append(temp - 1) break # 对指数降序排列 result.sort(reverseTrue) return result6.2 数学性质利用利用数论性质进一步优化def mathematical_optimization(k: int) - int: 利用数学性质进行优化 if k 1: return 1 # 特殊情况处理 if is_prime(k): # k为质数对应形式为 p^(k-1) return 2 ** (k - 1) # 将k分解质因数 factors prime_factors(k) # 根据分解结果构造最优指数分配 exponents [] temp k for p in sorted(factors, reverseTrue): while temp % p 0: exponents.append(p - 1) temp // p exponents.sort(reverseTrue) # 分配质数 result 1 primes [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] for i, exp in enumerate(exponents): if i len(primes): result * primes[i] ** exp else: # 需要更多质数此时数值会很大 return -1 # 或者使用大数处理 return result def is_prime(n: int) - bool: 判断是否为质数 if n 2: return False for i in range(2, int(n**0.5) 1): if n % i 0: return False return True7. 实际应用与扩展7.1 在密码学中的应用这类因子构造问题在密码学中有实际应用特别是在RSA加密算法中需要构造具有特定因子性质的数。7.2 在算法竞赛中的变种此类问题常见的变种包括构造具有恰好k个因子的第m小的数构造因子个数在某个范围内的数考虑因子和等其他数论函数7.3 工程实践建议在实际项目中处理类似问题时优先使用数学性质进行优化合理设置数值上限防止溢出使用记忆化搜索提高效率对边界情况进行充分测试通过系统学习这类问题的解法不仅能够提升算法竞赛能力还能加深对数论知识的理解为后续学习密码学、计算机代数等高级话题打下坚实基础。