蓝桥星数字 — 算法笔记
蓝桥星数字 — 算法笔记
题目来源
蓝桥杯模拟题 / 算法练习题
问题描述
蓝桥星数字
问题描述
地球上,我们习惯用十进制数字来记录万物,从个位、十位,逐级向上构成了我们熟悉的自然数体系。
然而,在遥远的蓝桥星,数字的排列和解读方式却与我们截然不同。蓝桥星人并不单纯地以数值大小来衡量一个数字,他们更注重数字内部蕴含的 “节奏感”。因此,对他们而言,任何一个有效的数字,其从左到右每一位上的数字奇偶性都必须是交替出现的。
例如,对于 10 这个数字,其十位是奇数 1,个位是偶数 0,呈现奇偶交替,因此 10 是个有效的数字。而对于 13 这个数字,其十位是奇数 1,个位也是奇数 3,不符合奇偶交替的条件,因此 13 不是个有效的数字。
根据这个规则,蓝桥星人的数字序列从 10 开始,依次为:10,12,14,16,18,21,23,25,27,29,30,…。
只不过,随着文明的发展,蓝桥星人需要一种方法,来快速找到第 N 个符合这种奇偶交替规则的数字,以满足其日益增长的数字处理需求。现在,请你帮助蓝桥星人,编写程序找出并输出第 N 个符合奇偶交替规则的数字。
输入格式
输入包含一个正整数 N,表示需要查找第 N 个符合规则的数字。
输出格式
输出一个整数,表示第 N 个符合奇偶交替规则的数字。
样例输入 1
1
样例输出 1
10
样例输入 2
11
样例输出 2
30
评测用例规模与约定
无
核心知识点
1. 计数原理(乘法原理)
每一位的候选数字数量:
| 位置 | 奇偶要求 | 候选数字 | 个数 |
|---|---|---|---|
| 首位 | 任意(不能是 0) | 1~9 | 9 |
| 后续位 | 与上一位奇偶相反 | 奇数 {1,3,5,7,9} 或 偶数 {0,2,4,6,8} |
5 |
k 位有效数字总数:9 × 5^(k-1)
2. 前缀树 / 字典序定位(按位分组)
所有有效数字按字典序排列,可视为一棵树:
root
/ / ... \ \ \ (首位: 9个候选)
1 2 3 4 5 6 7 8 9
/|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\
0 2 1 3 0 2 1 3 ... (第2位: 5个候选)
... (每层固定5个候选)
关键思想:第 N 个有效数字 = 在这棵前缀树上按字典序遍历的第 N 个节点。
每个候选节点后面跟着固定大小的"子树":
- 子树大小 =
5^(剩余位数) - 首位子树 =
5^(k-1)(每个 1~9 的候选后面都有同样大的子树) - 以此类推,每深入一位,子树大小 ÷ 5
3. 数位分组(确定目标数字的位数)
2位数: 9 × 5^1 = 45 个 (第 1 ~ 45 个)
3位数: 9 × 5^2 = 225 个 (第 46 ~ 270 个)
4位数: 9 × 5^3 = 1125 个 (第 271 ~ 1395 个)
...
从 2 位数开始累加,直到累计 ≥ N,就确定了目标位数。
4. 逐位构造(核心算法)
对于每一位:
- 计算子树大小:
subtree = 5^(k-1-pos) - 定位候选:
idx = (remain - 1) // subtree(0-based 序号) - 映射到真实数字:根据奇偶要求查表
- 更新 remain:
remain = (remain - 1) % subtree + 1 - 更新奇偶状态:下一位与当前位奇偶相反
候选数字映射:
| 条件 | 候选列表 | 序号 → 数字 |
|---|---|---|
| 首位 | [1, 2, 3, 4, 5, 6, 7, 8, 9] |
idx + 1 |
| 上一位是奇数 | [0, 2, 4, 6, 8](偶数) |
idx * 2 |
| 上一位是偶数 | [1, 3, 5, 7, 9](奇数) |
idx * 2 + 1 |
完整代码
n = int(input())
# Step 1: 确定位数 + 累计前面更短位数的总数
m = 9 * 5 # 2位数共45个
digit = 2
prev = 0 # 前面更短位数的累计个数
while prev + m < n:
prev += m
m = m * 5
digit += 1
n = n - prev # 变成"在当前位数中是第几个"
# Step 2: 逐位构造
num = 0
l = [[1, 3, 5, 7, 9], [0, 2, 4, 6, 8]] # l[0]=奇数, l[1]=偶数
odd = None # 上一位是奇数吗? None=首位
for i in range(digit, 0, -1):
j = i - 1 # 子树深度
num_new = (n - 1) // (5 ** j) # 0-based 序号
if odd == None: # 首位
num_new = num_new + 1 # 序号 → 1~9
odd = num_new % 2 # 记录奇偶
else:
num_new = l[odd][num_new] # 序号 → 查表得真实数字
odd = 1 - odd # 奇偶翻转
num = num * 10 + num_new
n = (n - 1) % (5 ** j) + 1
print(num)
复杂度分析
| 项目 | 复杂度 |
|---|---|
| 确定位数 | O(log₅ N) ≈ O(log N) |
| 逐位构造 | O(k) ≈ O(log N) |
| 总体 | O(log N) |
易错点
- 1 位数不算:有效数字从 10 开始,确定位数时 digit 初始化为 2
- 累计前面位数:找到位数后要减掉更短位数的总数,不能直接用原始 n
- 序号 vs 数字:
idx = (n-1)//subtree拿到的是候选列表中的序号(0-based),需要根据奇偶要求映射回真实数字 - 首位候选不含 0:后续位偶数候选
[0,2,4,6,8]含 0,但首位候选是[1~9]
类似题型
- 第 N 个回文数
- 第 N 个只有质数位的数字
- 第 N 个满足数位限制的数字
- 通用解法:按位数分组 + 逐位在前缀树上定位
浙公网安备 33010602011771号