一尘不染

Google面试:块的安排

algorithm

N个块的高度为1…N。您可以用几种方式将这些块连续排列,以便从左侧看时,您只能看到L块(其余部分被较高的块所隐藏),而从右侧看时,您只能看到R块?实施例给出的N=3, L=2, R=1,只有一个装置{2, 1, 3}N=3, L=2, R=2有两种方法{1, 3, 2}{2, 3, 1}

我们应该如何通过编程解决这个问题?有什么有效的方法吗?


阅读 207

收藏
2020-07-28

共1个答案

一尘不染

这是一个计数问题,而不是构造问题,因此我们可以使用递归来解决它。由于问题有两个自然的部分,即从左侧看和从右侧看,因此将其分解并首先解决一个问题。

b(N, L, R)是解决方案的数量,并让f(N, L)是安排数N块,这样L是从左边可见。首先考虑一下,f因为它更容易。

方法1

让我们获取初始条件,然后进行递归。如果所有内容都可见,则必须按顺序增加它们的顺序,因此

f(N, N) = 1

如果假定可见块多于可用块,那么我们无能为力,因此

f(N, M) = 0 if N < M

如果只有一个块可见,则将最大的块放在第一位,然后其他块可以按任何顺序排列,因此

f(N,1) = (N-1)!

最后,为进行递归,请考虑最高块的位置,例如N位于k左侧的第th个位置。然后选择以某种(N-1 choose k-1)方式出现在其前面的块,对这些块进行排列,以便L-1从左侧完全可见,并按自己喜欢的顺序对其N-k后面的块进行排序N,得到:

f(N, L) = sum_{1<=k<=N} (N-1 choose k-1) * f(k-1, L-1) * (N-k)!

事实上,自f(x-1,L-1) = 0x<L,我们不妨开始kL代替1

f(N, L) = sum_{L<=k<=N} (N-1 choose k-1) * f(k-1, L-1) * (N-k)!

正确,这样就可以理解较容易的知识了,让我们f来解决较难的知识b。再次使用基于最高块的位置的递归,再次说N是位于k左侧。和以前一样,以某种N-1 choose k-1方式选择前面的块,但是现在分别考虑该块的每一侧。对于k-1剩余的块N,请确保它们完全L-1可见。对于N-k正确的块N,确保R-1可见,然后颠倒顺序f。因此,答案是:

b(N,L,R) = sum_{1<=k<=N}  (N-1 choose k-1) * f(k-1, L-1) * f(N-k, R-1)

f上面完全解决了。同样,许多方面将是零,所以我们只希望采取k这样k-1 >= L-1N-k >= R-1得到

b(N,L,R) = sum_{L <= k <= N-R+1}  (N-1 choose k-1) * f(k-1, L-1) * f(N-k, R-1)

方法2

我再次考虑了这个问题,发现了一种更好的方法来避免求和。

如果您以相反的方式解决问题,即考虑添加最小的块而不是最大的块,则重复的过程f将变得更加简单。在这种情况下,如果初始条件相同,则重复发生为

f(N,L) = f(N-1,L-1) + (N-1) * f(N-1,L)

其中第一项,f(N-1,L-1)来自将最小的块放置在最左侧的位置,从而增加了一个可见的块(因此L减小为L-1),第二项,则(N-1) * f(N-1,L)用于将最小的块放置在任何N-1非前部位置,在这种情况下,它是不可见的(因此L保持不变)。

这种递归的优点是总递减N,尽管例如,它使查看某些公式变得更加困难f(N,N-1) = (N choose 2)。从上一个公式可以很容易地看出这个公式,尽管我不确定如何从这种更简单的重复中很好地得出它。

现在,要回到最初的问题并解决b,我们还可以采用其他方法。与其将之前的总和考虑在内,不如将可视块视为数据包中的数据包,这样,如果一个数据块从左侧可见,则其数据包将包括其右侧的所有块以及从左侧可见的下一个块的前面,以及类似地,如果从右侧可见一个块,则其数据包将包含其左侧的所有块,直到从右侧可见的下一个块。对除了最高的街区之外的所有街区进行此操作。这使得L+R包。给定数据包后,您可以简单地通过反转块的顺序从左侧移动到右侧。因此一般情况b(N,L,R)实际上简化为解决情况b(N,L,1) = f(N,L)然后选择将哪些数据包放在左侧,将哪些数据包放在右侧。因此,我们有

b(N,L,R) = (L+R choose L) * f(N,L+R)

同样,与以前的版本相比,这种重新制定具有一些优势。将后两个公式放在一起,可以很容易地看到整个问题的复杂性。但是,尽管其他人可能会不同意,但我仍然更喜欢采用第一种方法来构造解决方案。总而言之,这表明解决问题的方法不止一种。


斯特林数是多少?

就像杰森指出的那样,f(N,L)数字恰好是第一类(无符号)斯特林数字。可以从每个递归公式中立即看到这一点。但是,能够直接看到它总是很高兴,所以这里是。

第一类的(无符号)斯特林数,表示S(N,L)计数NL周期的排列数。给定以循环符号表示的置换,我们通过以该循环中编号最大的循环开始该循环,然后以该循环的第一个数字递增的顺序对循环进行排序,从而以规范形式写入该置换。例如,排列

(2 6) (5 1 4) (3 7)

将以规范形式写为

(5 1 4) (6 2) (7 3)

现在放下括号,并注意,如果这些是块的高度,那么从左侧开始可见的块数就是循环数!这是因为每个循环的第一个数字会阻塞该循环中的所有其他数字,并且每个连续循环的第一个数字在前一个循环之后是可见的。因此,这个问题实际上只是要求您找到斯特林数字公式的一种偷偷摸摸的方法。

2020-07-28