📌  相关文章
📜  使用给定数字的位数获得第k个最小数字(1)

📅  最后修改于: 2023-12-03 14:49:57.157000             🧑  作者: Mango

使用给定数字的位数获得第k个最小数字

问题描述

有一个整数 $n$ 表示一位数字的位数,现在我们需要求出由 $1$ 至 $9$ 组成的数字中,第 $k$ 小的数是多少。

输入格式

输入一行,包含两个整数 $n$ 和 $k$,用空格隔开,其中 $1 \leq n \leq 9$,$1 \leq k \leq 10^9$。

输出格式

输出一个整数,表示由 $1$ 至 $9$ 组成的数字中,第 $k$ 小的数是多少。

算法思路

这是一道数位 DP(动态规划)的题目,我们需要设计一个状态表示,并推导转移方程。

设 $f[i][j]$ 表示使用 $i$ 个数字,最小的数字为 $j$ 时,一共有多少个满足条件的数字。

显然,当 $i=1$ 时,$f[1][j]=1$($1 \leq j \leq 9$)。

当 $i>1$ 时,我们枚举最高位的数字 $j$,那么剩下的 $i-1$ 位数字只需要从 $1$ 至 $j$ 中任选即可。这里需要注意的是,当 $j=1$ 时,我们只能从 $1$ 中选,否则会产生前导零。

根据上述思路,我们得到了如下的转移方程:

$$ f[i][j]=\begin{cases} 1, & i=1 \ f[i-1][j-1], & j \geq 2 \ f[i-1][1], & j=1 \ \end{cases} $$

其中,$f[i-1][j-1]$ 表示将当前的最高位数字确定为 $j$,剩下的 $i-1$ 位数字的取值范围为 $[1,j-1]$;$f[i-1][1]$ 表示当前的最高位数字为 $1$,此时剩下的 $i-1$ 位数字只能从 $1$ 中选。

最终答案为所有 $f[n][j]$ 的和,$1 \leq j \leq 9$。

代码实现
n, k = map(int, input().split())

# 初始化状态
f = [[0] * 10 for _ in range(n + 1)]
for j in range(1, 10):
    f[1][j] = 1

# 动态规划计算状态
for i in range(2, n + 1):
    for j in range(1, 10):
        if j >= 2:
            f[i][j] = f[i-1][j-1]
        else:
            f[i][j] = f[i-1][1]
    for j in range(1, 10):
        f[i][j] += f[i][j-1]

# 计算答案
ans = 0
j = 1
while k > f[n][j]:
    k -= f[n][j]
    j += 1
    if j == 10:
        break
ans = j
for i in range(n - 1, 0, -1):
    for j in range(1, 10):
        if j >= 2:
            cnt = f[i][j-1]
        else:
            cnt = f[i][1]
        if k <= cnt:
            ans = ans * 10 + j
            break
        else:
            k -= cnt
print(ans)
参考文献