数字增殖问题 | 豆包MarsCode AI刷题

197 阅读4分钟

问题描述

给定一个正整数 n,定义一次"增殖"操作如下:

  • 将数字 n 转化为一个包含 1 到 n 的递增序列
  • 例如:当 n = 4 时,一次增殖后变为序列 [1, 2, 3, 4]

现给定三个正整数:

  • 初始数字 n
  • 增殖次数 k
  • 位置索引 p(从1开始计数)

请计算经过 k 次增殖操作后,序列中第 p 个位置的数字。如果 p 超出序列长度,则返回 -1

示例说明

示例 1:

测试样例

样例1:

输入:n = 4 ,k = 3 ,p = 19
输出:3
解释:
第1次增殖:4 -> [1,2,3,4]
第2次增殖:[1,2,3,4] -> [1, 1,2, 1,2,3, 1,2,3,4]
第3次增殖:得到最终序列 [1, 1, 1,2, 1, 1,2, 1,2,3, 1, 1,2, 1,2,3, 1,2,(3),4] -> ,第19个位置的数字是3

样例2:

输入:n = 3 ,k = 2 ,p = 5
输出:2
解释:
第1次增殖:3 -> [1,2,3]
第2次增殖:[1,2,3] -> [1, 1,2, 1,(2),3]
第5个位置的数字是2

样例3:

输入:n = 5 ,k = 1 ,p = 7
输出:-1
解释:
第1次增殖:5 -> [1,2,3,4,5]
序列长度为5,p=7超出范围,返回-1

问题分析

  1. 数据规模

    • 每次增殖操作都会显著增加序列长度。
    • 假设初始 n 为 4:
      • 第一次增殖后长度为 4。
      • 第二次增殖后长度为 1+2+3+4=10。
      • 第三次增殖后长度为 1+(1+2)+(1+2+3)+(1+2+3+4)=20。
    • 可以发现,序列长度随着增殖次数呈指数增长,不过在该文章分布前,本题使用直接构造的方法是可以通过的。
  2. 直接构造法的局限性

    • 构造完整序列需要消耗大量时间和内存,尤其是在增殖次数 k 较大时。
    • 一个更高效的解决方案是定位目标元素而不是生成完整序列。
  3. 关键性质:分治法

    • 每次增殖操作中,序列可以拆分为若干子区间。
    • 每个子区间对应于前一次增殖结果中的一个数字。
    • 例如:
      • 初始 n=3,第一次增殖后为 [1,2,3]。
      • 第二次增殖后为: [1,(1,2),(1,2,3)]
    • 因此,通过判断目标位置 p 所在的子区间,可以递归找到结果。

解决方案

下面介绍两种方法:暴力法分治递归法

方法一:暴力生成序列
  1. 核心思想

    • 每次增殖操作生成完整的序列。
    • 最终在序列中直接查找目标位置 p。
  2. 代码实现

from itertools import chain 

def solution(n, k, p):
    l = [n]
    f = lambda x: [i for i in range(1, x + 1)]
    for _ in range(k):
        t = list(map(f,l))
        l = list(chain.from_iterable(t))
    return l[p - 1] if p <= len(l) else -1

if __name__ == "__main__":
    # Add your test cases here
    print(solution(4, 3, 19) == 3)

  1. 优缺点
  • 优点:逻辑简单,易于实现。
  • 缺点:在增殖次数多时无法处理大数据。
方法二:分治递归法
  1. 核心思想

    • 不直接构造整个序列,而是通过递归计算区间长度,确定目标位置 p 的数字。

    • 递归步骤:

      • 每次增殖,序列由若干子区间构成。
      • 确定目标位置 p 所在的子区间,递归查找该子区间内的元素。
  2. 代码实现

def sequence_length(n, k):
    if k == 0:
        return 1
    return sum(sequence_length(i, k - 1) for i in range(1, n + 1))


def solution(n, k, p):
    if k == 0:
        return n if p == 1 else -1

    current_pos = 0
    for i in range(1, n + 1):
        # 当前子区间的长度
        sub_length = sequence_length(i, k - 1)
        if current_pos + sub_length >= p:
            # 进入子区间查找
            return solution(i, k - 1, p - current_pos)
        current_pos += sub_length

    return -1

if __name__ == "__main__":
    # Add your test cases here
    print(solution(4, 3, 19) == 3)
    print(solution(5, 1, 7) == -1)
    print(solution(3, 2, 5) == 2)

总结

  1. 核心问题
    • 本题的核心是如何高效处理“指数增长”的序列。
    • 分治法通过递归定位目标位置,避免了生成完整序列的高成本。
  2. 解决思路
    • 暴力法适用于增殖次数 k 较小的情况。
    • 优化法适用于增殖次数较大、目标位置 p 较深的情况。