algorithm - 如何找到第 x 个十进制数?
问题描述
Hackerrank 有一个称为十进制数的问题,它本质上是具有 0-9 位数值的数字,但使用 2 的幂进行幂运算。问题要求我们显示第 x 个十进制数。这个问题还有另一个转折点。多个十进制数可以等于同一个十进制数。例如,十进制的 4 可以是十进制的 100、20、12 和 4。
起初,我认为找出给定十进制数有多少个十进制数会很有帮助。我查阅了这篇文章以获得一些帮助(https://math.stackexchange.com/questions/3540243/whats-the-number-of-decibinary-numbers-that-evaluate-to-given-decimal-number)。这篇文章有点难以理解,但后来我也意识到,即使我们有一个十进制数可以有多少个十进制数,这也无助于找到它们(至少据我所知),这是最初的目标问题。
我确实意识到,对于任何十进制数,它的最大十进制数只是它的二进制表示。例如,对于 4,它是 100。因此,蛮力方法是检查每个十进制数在此范围内的所有数字,看看它们的十进制表示是否计算为给定的十进制数,但很明显,这种方法永远不会由于输入约束将 x 定义为从 1 到 10^16,因此通过。不仅如此,我们还必须为 aq 数量的查询找到第 x 个十进制数,其中 q 从 1 到 10^5。
这个问题属于 dp 部分,但我很困惑如何使用 dp 或者它是如何可能的。为了计算第 x 个十进制数 q 次(在上面的蛮力方法中进行了描述),最好使用一个表格(就像问题所暗示的那样)。但为此,我们需要存储和计算 10^16 个整数,因为这就是 x 的大小。假设一个整数是 4 字节,4B * 10^16 ~= 4B * (2^3)^16 = 2^50 字节。
有人可以解释一下如何以最佳方式解决这个问题。我还是 CP 的新手,所以如果我在某些方面犯了错误,请告诉我。
(有关完整的问题陈述,请参见下面的链接): https ://www.hackerrank.com/challenges/decibinary-numbers/problem
解决方案
这可以用大约 80 MB 的数据来解决。我不会给出代码,但我会解释策略。
构建一个查找,为您提供使用前几位count[n][i]
获取十进制数的多种方法。您首先在任何地方插入,然后在. 现在开始使用规则填写:n
i
0
1
count[0][0]
count[n][i] = count[n][i-1] + count[n - 2**i][i-1] + count[n - 2*2**i][i-1] + ... + count[n - 9*2**i][i-1]
事实证明,您只需要前 19 位数字,并且n
最多只需要2**19-1
. 并且计数都适合 8 byte long
s。
一旦你有了它,创建第二个数据结构count_below[n]
,它是多少十进制数的计数将给出小于n
. 使用与以前相同的范围n
。
现在查找进行如下。首先,您进行二进制搜索count_below
以找到最后一个小于您的目标数字的值。从您的查询中减去count_below
,您知道您想要该值的哪个十进制数。
接下来,搜索count[n][i]
以找到i
这样的方式,您可以使用i
数字而不是更少的方式获得目标查询。这将是您答案的首位数字的位置。然后,您从查询中减去count[n][i-1]
(所有数字较少的十进制数)。然后减去 off count[n-2**i][i-1]
, count[n-2* 2**i][i-1]
, ...count[n-8*2**i][i-1]
直到找到前导数字。现在,您从该值中减去该数字的贡献,并重复逻辑以找到具有较少数字的较小值的正确十进制。
这是一个工作示例来澄清。首先是前 3 位及以下的数据结构2**3 - 1
:
count = [
[1, 1, 1, 1], # sum 0
[0, 1, 1, 1], # sum 1
[0, 1, 2, 2], # sum 2
[0, 1, 2, 2], # sum 3
[0, 1, 3, 4], # sum 4
[0, 1, 3, 4], # sum 5
[0, 1, 4, 6], # sum 6
[0, 1, 4, 6], # sum 7
]
count_below = [
0, 1, 2, 4, 6, 10, 14, 20, 26, ...
]
让我们找到第20个。
count_below[6]
是 14 和count_below[7]
20 所以我们的十进制和是6
。- 我们想要
20 - count_below[6] = 6
十进制和 6 的 th 十进制。 count[6][2]
是 4count[6][3]
而是 6 所以我们有一个非零的第三个数字。- 我们想要
count[6][3] - count[6][2] = 2
第三位非零的 。 count[1][6 - 2**2]
是 2,所以 2 有第三个数字 1。- 第三个数字是1
- 我们现在正在寻找其十进制和为 的第二个十进制
2
。 count[2][1]
是 1 和count[2][2]
2 所以它有一个非零的第二个数字。- 我们想要
count[2][2] - count[2][1] = 1
第二个数字非零的 st。 - 第二个数字是1
- 其余为 0,因为
2 - 2**1 = 0
.
因此你会发现答案是 110。
现在对于这么小的数字,这是很多工作。但即使是最困难的查找,您也只需要大约 20 步的二进制搜索来找到您的十进制和,另外 20 步来找到第一个非零数字的位置,并且对于这些数字中的每一个,您将必须进行 1-9 次不同的计算才能找到该数字。这意味着只有数百次计算才能找到数字。
推荐阅读
- vba - VBA 错误处理程序:On Error Resume 在处理程序中不起作用
- list - 如何在 Rascal 中将“IO 操作”与列表理解或映射器一起使用?
- sql - 如何从包含阿拉伯文或波斯文字母的表格中选择所有行?
- .net - 无法加载文件或程序集以使用 NPOI 库中的 XSSFWorkbook 类
- node.js - 在 Heroku 部署中未收到令牌。开发很好
- excel - 使用 Powershell,如何对 excel 列中的值求和并按另一个变量的值排序?
- c++ - 并行矢量调整大小没有加快
- angular - 当找不到对象时,Angular 服务可观察对象的正确行为是什么?
- spring-boot - Spring Boot 服务调用将不会通过 HTTP Proxy (Fiddler) 运行
- ruby-on-rails - 收到订单后更新订单商品