【问题标题】:Find how many numbers meet the constraints in a range找出有多少数字满足范围内的约束
【发布时间】:2015-10-28 13:22:40
【问题描述】:

给定 2 个整数 l 和 r,计算 [l, r] 中有多少个数满足这些约束

1) 这个数应该能被 7 整除

2) 号码至少包含三位数字 7

3) 该数字包含的数字 7 比数字 4 多

777、774746 满足这些限制,7771、77、747474 不满足。

使用蛮力可以很容易地找到答案,但是当范围非常大时,可能会花费很多时间。

我认为动态规划可以帮助解决这个问题,但我想不出解决方案

谁能给我一些指导?

【问题讨论】:

  • 到目前为止你得到了什么?我的意思是你所说的蛮力是什么意思?恐怕除非你做一些非常极端的事情,否则没有什么可以改进的。不幸的是,十进制表示对计算机不是很友好:/
  • 我的意思是逐个测试 [l, r] 中的每个数字,如果我必须检查 [777, 777777777777777] 中的数字,那么可能需要很多时间,所以我正在尝试想办法优化它

标签: c++ dynamic-programming


【解决方案1】:

取自原始蛮力版本:

Iterate with i over numbers between [l,r]
    Use modulo to check if i is divisible by 7
    Use modulo and division to get counts of digits in i
    digit_count(7) >= 3
    digit_count(7) > digit_count(4)

这是我想出的一些想法......

1。仅使用 7 的倍数,隐含满足第一个标准:

这个真的很简单。我们可以改进它以仅使用可被7 整除的i。如果我给你一个数字x 并要求你生成可被n 整除的数字,直到你达到y,那么你最好这样做:

for (auto i = x + x % n; i < y; i += n)

因此,对于7lr 之间的倍数,您需要做的就是运行循环for (auto i = l + l % 7; i &lt; r; i += 7),这将使您的速度比蛮力版本提高7 倍。

2。记住数字计数

无需执行大量除法和模数运算即可计算所经过的每个数字的位数。因为你知道你增加了多少,你也知道什么数字变成什么。这样,您只需将起始编号拆分为数字(例如l % 7 + l)。

现在我们要存储的不是数字的数量,而是实际上非常类似于 BCD 的东西——代表我们当前正在使用的数字的数字数组。然后你会得到类似std::vector&lt;int&gt; 的东西,表示[7, 7, 2, 4, 5, 7] 的数组用于数字772457。现在你需要做的就是在每次增加循环计数器时使用数组中的 BCD 算法,去[7, 7, 2, 4, 5, 7] + 6 = [7, 7, 2, 4, 6, 3]

我们还需要存储两个ints - sevensfours。在初始化阶段,一旦您将第一个数字“分解”到数组中,您只需遍历它并为每个7 递增sevens,为每个4 递增fours。你只需保持这些数字是最新的:随着数组的每次更新,你会为你带走的每个4 递减fours,并为你在数组中创建的每个4 递增它。号码7 也是如此。然后您可以比较sevens >= 3 && sevens > fours 并快速知道结果。

有趣的是,这对复杂性没有理论上的改进,而且它可能不起作用,但我怀疑它应该......代码量很大,所以我不去提供它。您最终可能会使用倒置的 BCD 数组或从 r 迭代范围的结尾开始,因此您无需调整数组的大小。也许您可以提出更多改进和调整。但是,我强烈怀疑通过这种方式可以渐近地降低解决方案的复杂性。

3。更多想法

现在这根本不是动态编程。或者是吗?如果您考虑一下,我有一种直觉,将数字数组作为 BCD 的想法现在可以转换为一个问题,您可以在其中查找包含给定组合的排列。您可以从中制作图表并进行搜索。这就是你要动态的地方。但是,我担心这会使帖子变得更长……

但我已经对此产生了第一个疑问,那就是检查可被 7 整除,然后将其应用于图中找到的所有数字(该图仅支持标准 2 和 3 的性质和产量所有包含组合的数字)。所以最后归结为算法应该支持的范围大小以及在这些范围中满足第一个标准的数字与满足第二个和第三个标准的数字的比率。

编辑: 从那以后,我发现我计算符合标准的数字计数的想法是不正确的。一些小的比较表:

|   range    |      numbers f/ c2     | c2_groups | c2_total | c1_total |
|   0 - 1k   |         777            |         1 |        1 |     ~143 |
|  1k - 10k  | _777, 7_77, 77_7, 777_ |         4 |       40 |    ~1286 |
| 10k - 100k |   __777, _7_77, ...    |        10 |     1000 |   ~12857 |

其中numbers f/ c2 是满足条件 2 的数字,c2_groups 是数字中任意数字和 7 的可能组合的计数,cx_total 是满足范围内标准 x 的数字的总数。

这样看来,首先按位数标准进行过滤是否有效,这似乎是一个非常值得怀疑的问题。我想这需要一些数学分析,这比实现解决方案需要更长的时间......

空间搜索

通过与方法#2 等效的状态,可以在数字范围内进行 DFS。它不会增加 7,而是存储一个数字向量并根据可移动的偏移量在其中增加值,例如

increment [1, 0, 7, _] -> [1, 0, 8, _]
                 ^               ^

这就是算法将在核心循环中执行的操作。然后,您可以检查当前数字矢量设置是否可以满足标准 - 例如[0, p, _, _] 可以实现它们,而[0, 0, p, _] 不能(p 是被指向的元素)。这样,您将不断增加可能的最高数字,跳过很多数字。每次有可能满足要求时,您都会增加偏移量并重复该过程:

push [7, 7, _, _] -> [7, 7, 0, _]
         ^                  ^

一旦您处于最低有效数字位置,您还将开始检查每个候选人的 7 整除性。您可以尝试将数字转换为 int 并使用模数或使用某种除法算法(这些使用数字,所以这是一个令人愉快的巧合)。

这样,您将获得一个通过所有条件的数字并将其返回。现在您可能会遇到用尽给定数字范围内的所有数字的情况。在这种情况下,您需要将偏移量向后移动一位:

pop [7, 7, 7, 9] -> [7, 7, 7, _]
              ^            ^

现在,您将使用increment,看到[7, 7, 8, _] 可以满足条件并且再次使用push。然后运行0, 1, 2, ... 序列,直到你到达7,看到7787 在第二和第三个条件下都可以,但是被7 划分失败。等等……

您还需要检查您是否尚未超过 r 限制。我想这可以通过将r 拆分为数字并将其与最高有效数字进行比较来以相当理智的方式完成。

鉴于我们没有对此进行数学分析,而且这仍然需要处理很多数字(尤其是在 7 是最低有效数字的情况下),我想知道这是否真的值得实施。但这也不是超级复杂的事情。祝你好运!

【讨论】:

  • 感谢您的详细信息,我之前也尝试过您的第一个想法,但我认为还有一些方法可以优化它。
  • 编辑了帖子,带有正确数字生成的图表绝对是一种方法......稍后我会回复您并提供更多信息
  • 抱歉,您怎么知道在这 130k 个数字周围只有 100 个具有三个或更多 7 位的数字?
  • 好吧,130k 是 900k / 7,即 (1M - 100k) / 7。由于除法的定义,我认为这是给定范围大小的倍数的近似计数。近似是因为存在舍入,并且范围大小不足以确定它(例如,在 40-50 范围内有 2 个 7 的倍数,在 50-60 范围内只有 1 个倍数)。
  • 而 100 是旧的部分排列技巧的结果:100000 到 999999 之间的数字有 6 位:_ _ _ _ _ _。您的规则要求其中至少三个为 7 - 您最终得到 7 7 7 _ _ _ (顺序无关紧要)。所以有 10 * 10 * 10 个组合满足第二个标准。但是,有可能不允许使用相同的四数,所以实际的解决方案是 7 7 7 *4 _ _ 这会让你得到 9 * 10 * 10 = 900 的排列,而我在原来的时候错了发布... :)
【解决方案2】:

对于 1:if(yourint % 7 == 0)

对于2:检查此链接split int into digits,检查数字是否等于7并计数到3。

对于 3:使用 if 扩展链接 2,数字等于 7 或 4,而不是 counter++

最后你应该检查你的计数器(7 和 4)哪个是最高的。

【讨论】:

  • 这是我做的,但是如果范围很大,那么我必须等待很长时间才能得到答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-05-23
  • 1970-01-01
  • 2021-01-28
  • 2020-11-15
  • 2012-12-28
  • 1970-01-01
  • 2018-10-16
相关资源
最近更新 更多