"P1151 子数整数"是一道典型的进制转换与数字处理类编程题目,主要考察对整数位运算和进制转换的理解能力。这类题目在信息学竞赛(如NOIP)和算法训练中经常出现,属于基础但容易出错的题型。
题目给定一个十进制整数K,要求找出所有满足特定条件的五位数:这个五位数的三个连续子数(即前三位、中间三位和后三位)都能被K整除。例如对于五位数12345:
从数学角度,我们可以将五位数表示为N=abcde(a≠0),则:
题目要求这三个子数都能被K整除,即:
最直观的解法是遍历所有五位数(10000-99999),检查每个数是否满足条件:
python复制def find_numbers(K):
result = []
for num in range(10000, 100000):
a = num // 10000
b = (num // 1000) % 10
c = (num // 100) % 10
d = (num // 10) % 10
e = num % 10
sub1 = 100*a + 10*b + c
sub2 = 100*b + 10*c + d
sub3 = 100*c + 10*d + e
if sub1 % K == 0 and sub2 % K == 0 and sub3 % K == 0:
result.append(num)
return result
注意:虽然暴力法简单直接,但当K较小时(如K=1),会输出所有五位数,效率较低。
更高效的方法是利用模运算性质进行构造:
基于此可以构建递推关系:
python复制def find_numbers_optimized(K):
result = []
for sub1 in range(100, 1000):
if sub1 % K != 0:
continue
a, b, c = sub1 // 100, (sub1 // 10) % 10, sub1 % 10
for d in range(0, 10):
sub2 = (sub1 % 100)*10 + d
if sub2 % K != 0:
continue
for e in range(0, 10):
sub3 = (sub2 % 100)*10 + e
if sub3 % K == 0:
num = sub1 * 100 + d * 10 + e
result.append(num)
return result
提取数字各位的几种方法对比:
| 方法 | 代码示例 | 效率 | 适用场景 |
|---|---|---|---|
| 字符串转换 | digits = list(str(num)) |
较低 | 代码简洁,但类型转换开销大 |
| 数学运算 | d = (num//10**n)%10 |
高 | 竞赛首选,无类型转换 |
| 迭代取余 | while num>0: digits.append(num%10); num//=10 |
中 | 需要逆序数字时 |
需要特别注意的边界情况:
| 输入K | 预期输出 | 验证要点 |
|---|---|---|
| 15 | 多个解如22555 | 普通情况验证 |
| 100 | 无解 | 大K值测试 |
| 1 | 所有五位数 | 边界值测试 |
| 123 | 可能无解 | 较大质数测试 |
建议使用assert进行结果验证:
python复制def test():
assert len(find_numbers(15)) == len(find_numbers_optimized(15))
assert find_numbers(100) == []
assert len(find_numbers(1)) == 90000
print("All tests passed!")
对于固定K的多次查询,可以预先计算并缓存结果:
python复制from functools import lru_cache
@lru_cache(maxsize=None)
def get_solutions(K):
return find_numbers_optimized(K)
对于极大范围的搜索(如扩展到更多位数),可使用多进程:
python复制from multiprocessing import Pool
def parallel_search(K, start, end):
# 分片搜索实现
pass
观察发现:
将五位数扩展为N位数,检查所有长度为M的连续子数是否满足条件。这时需要:
将十进制扩展为任意进制B:
python复制def baseB_subnumber(num, B, digits):
# 实现B进制下的子数计算
pass
这类算法可用于:
python复制# 错误:忽略a≠0的条件
num = 0 # 错误地包含00000-09999
python复制# 错误:错误截取子数
sub2 = (num // 10) % 1000 # 错误地得到后四位中的前三位
python复制print(f"num={num}, sub1={sub1}, sub2={sub2}, sub3={sub3}")
python复制assert calculate_subnumber(12345, 1, 3) == 123
python复制import pdb; pdb.set_trace()
cpp复制vector<int> findNumbers(int K) {
vector<int> res;
for(int num=10000; num<=99999; ++num) {
int sub1 = num / 100;
int sub2 = (num / 10) % 1000;
int sub3 = num % 1000;
if(sub1%K==0 && sub2%K==0 && sub3%K==0)
res.push_back(num);
}
return res;
}
java复制public static List<Integer> findNumbers(int K) {
List<Integer> result = new ArrayList<>();
for(int num=10000; num<=99999; num++) {
int a = num / 10000;
// 其他位提取类似...
}
return result;
}
javascript复制function findNumbers(K) {
return Array.from({length: 90000}, (_,i) => i+10000)
.filter(num => {
const s = num.toString();
return [s.slice(0,3), s.slice(1,4), s.slice(2)].every(
sub => parseInt(sub)%K === 0);
});
}
输入输出优化:
ios::sync_with_stdio(false)sys.stdin.readline提前终止条件:
python复制if K > 999: return [] # 无解提前返回
位运算加速:
python复制# 替代除法和取模
sub1 = (num >> 2) # num//4
内存预分配:
python复制result = [0]*90000 # 最大可能解数量
对于初学者,建议按以下步骤掌握:
推荐练习题目: