ruye07

导航

蓝桥星数字 — 算法笔记

蓝桥星数字 — 算法笔记

题目来源

蓝桥杯模拟题 / 算法练习题

问题描述

蓝桥星数字
问题描述
地球上,我们习惯用十进制数字来记录万物,从个位、十位,逐级向上构成了我们熟悉的自然数体系。
然而,在遥远的蓝桥星,数字的排列和解读方式却与我们截然不同。蓝桥星人并不单纯地以数值大小来衡量一个数字,他们更注重数字内部蕴含的 “节奏感”。因此,对他们而言,任何一个有效的数字,其从左到右每一位上的数字奇偶性都必须是交替出现的。
例如,对于 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. 逐位构造(核心算法)

对于每一位:

  1. 计算子树大小subtree = 5^(k-1-pos)
  2. 定位候选idx = (remain - 1) // subtree(0-based 序号)
  3. 映射到真实数字:根据奇偶要求查表
  4. 更新 remainremain = (remain - 1) % subtree + 1
  5. 更新奇偶状态:下一位与当前位奇偶相反

候选数字映射:

条件 候选列表 序号 → 数字
首位 [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. 1 位数不算:有效数字从 10 开始,确定位数时 digit 初始化为 2
  2. 累计前面位数:找到位数后要减掉更短位数的总数,不能直接用原始 n
  3. 序号 vs 数字idx = (n-1)//subtree 拿到的是候选列表中的序号(0-based),需要根据奇偶要求映射回真实数字
  4. 首位候选不含 0:后续位偶数候选 [0,2,4,6,8] 含 0,但首位候选是 [1~9]

类似题型

  • 第 N 个回文数
  • 第 N 个只有质数位的数字
  • 第 N 个满足数位限制的数字
  • 通用解法:按位数分组 + 逐位在前缀树上定位

posted on 2026-05-21 21:15  ruye07  阅读(6)  评论(0)    收藏  举报