📅  最后修改于: 2023-12-03 14:49:57.157000             🧑  作者: Mango
有一个整数 $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)