假设我们将考虑具有长度2n
并且n
可能约为的二进制数1000
。我们正在寻找具有以下属性的kth
数字(k 受 限制):10^9
- 金额
1's
等于金额0's
可以描述如下的数量:#(1) = #(0)
- 这个数字的每个前缀必须至少
0's
包含1's
. 否定句子后可能更容易理解,即:没有前缀可以包含1's
超过0's
。
基本上就是这样。因此,为了清楚起见,让我们举个例子:
n=2
,k=2
我们必须取长度的二进制数2n
:
0000
0001
0010
0011
0100
0101
0110
0111
1000
and so on...
现在我们必须找到2nd
满足这两个要求的数字。所以我们看到0011
的是第一个,0101
也是第二个。如果我们更改k=3
,则答案不存在,因为存在具有相同数量的相反位的数字,但是对于0110
,存在前缀011
,因此数字不满足第二个约束,并且所有具有1
最高有效位的数字都相同。
那么到目前为止我做了什么来找到算法?
好吧,我的第一个想法是生成所有可能的位设置,并检查它是否具有这两个属性,但生成它们都将采用O(2^(2n))
这不是n=1000
.
此外,我意识到没有必要检查所有小于0011
for n=2
、000111
forn=3
等的数字……坦率地说,那些最高有效位的一半仍然“未触及”的数字,因为这些数字不可能满足#(1) = #(0)
条件。使用它我可以减少n
一半,但它没有多大帮助。而不是 2 * forever 我有永远运行的算法。它仍然O(2^n)
是复杂的,这太大了。
对算法有什么想法吗?
结论
这篇文章是我在阅读 Andy Jones 帖子后的想法而创建的。
首先,我不会发布我使用过的代码,因为它是来自 Andy 的帖子Kasa 2009的以下文档中的第 6 点。您所要做的就是nr
按照我所描述的那样考虑k
。Unranking Dyck words 算法,将帮助我们更快地找到答案。但是,它有一个瓶颈。
while (k >= C(n-i,j))
考虑到这一点n <= 1000
,加泰罗尼亚语数可能相当大,甚至C(999,999)
. 我们可以使用一些大数算术,但另一方面我想出了一个小技巧来超越它并使用标准整数。
我们不想知道加泰罗尼亚数实际上有多大,只要它大于k
. n x n
所以现在我们将在表格中创建缓存部分和的加泰罗尼亚数字。
... ...
5 | 42 ...
4 | 14 42 ...
3 | 5 14 28 ...
2 | 2 5 9 14 ...
1 | 1 2 3 4 5 ...
0 | 1 1 1 1 1 1 ...
---------------------------------- ...
0 1 2 3 4 5 ...
生成它非常简单:
C(x,0) = 1
C(x,y) = C(x,y-1) + C(x-1,y) where y > 0 && y < x
C(x,y) = C(x,y-1) where x == y
所以我们只能看到这个:
C(x,y) = C(x,y-1) + C(x-1,y) where y > 0 && y < x
可能导致溢出。
让我们停在这一点上并提供定义。
k-flow
- 这不是整数的真正溢出,而是值C(x,y)
大于的信息k
。
我的想法是在每次运行上述公式后检查是否C(x,y)
大于k
或任何总和分量-1
。如果是我们把-1
它作为一个标记,那就k-flow
已经发生了。我想很明显,如果k-flow
数字与任何正数相加,它仍然是k-flowed
特别是 2 个k-flowed
数字的总和k-flowed
。
最后我们要证明的是,不可能产生真正的溢出。真正的溢出只有在我们总结a + b
它们中的哪一个时才会发生,k-flowed
但它们总和产生了真正的溢出。
当然这是不可能的,因为最大值可以描述为a + b <= 2 * k <= 2*10^9 <= 2,147,483,647
这个不等式中的最后一个值是带符号的 int 值。我还假设 int 有 32 位,就像我的情况一样。