取自原始蛮力版本:
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)
因此,对于7 在l 和r 之间的倍数,您需要做的就是运行循环for (auto i = l + l % 7; i < r; i += 7),这将使您的速度比蛮力版本提高7 倍。
2。记住数字计数
无需执行大量除法和模数运算即可计算所经过的每个数字的位数。因为你知道你增加了多少,你也知道什么数字变成什么。这样,您只需将起始编号拆分为数字(例如l % 7 + l)。
现在我们要存储的不是数字的数量,而是实际上非常类似于 BCD 的东西——代表我们当前正在使用的数字的数字数组。然后你会得到类似std::vector<int> 的东西,表示[7, 7, 2, 4, 5, 7] 的数组用于数字772457。现在你需要做的就是在每次增加循环计数器时使用数组中的 BCD 算法,去[7, 7, 2, 4, 5, 7] + 6 = [7, 7, 2, 4, 6, 3]。
我们还需要存储两个ints - sevens 和fours。在初始化阶段,一旦您将第一个数字“分解”到数组中,您只需遍历它并为每个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 是最低有效数字的情况下),我想知道这是否真的值得实施。但这也不是超级复杂的事情。祝你好运!