编号为1,2,...,n的发动机位于左侧的线上,并且需要在发动机离开右侧轨道时重新排列(置换)发动机。支线轨道上的引擎可以留在那里或沿着正确的轨道发送,但它永远不会被发送回进入的轨道。例如,如果n = 3,并且我们在左侧轨道上编号为1,2,3的引擎,那么3首先进入支线轨道。然后我们可以发送2到分支,然后在右边的路上,然后在途中发送3,然后1,获得新的顺序1,3,2。 我们必须找到特定n的所有可能的排列。
对于n = 1,回答= 1;
对于n = 2回答= 2;
对于n = 3回答= 5;
没有找到任何一般性。 使用堆栈实现非常有用。
但欢迎任何解决方案。
P.S。这不是一个家庭作业问题,因为我是一个自学成才的人。
答案 0 :(得分:2)
这是我尝试递归解决方案(参见Java代码中的注释):
private static int result = 0;
private static int n = 3;
public static void g(int right,int spur){
if (right == n) // all trains are on the right track
result++;
else if (right + spur < n) // some trains are still on the left track
g(right,spur + 1); // a train moved from the left track to the spur
if (spur > 0) // at least one train is on the spur
g(right + 1,spur - 1); // a train moved from the spur to the right track
// (also counts trains moving directly from the left to right track)
}
public static void main (String[] args){
g(0,0);
System.out.println(result); // 5
}
上面的递归解决方案实际上考虑了每种可能性。对于组合解,我们考虑在{ - 1}}运动的所有组合,其中相邻的这些运动相当于直接从左到右轨道的运动。有n
个此类组合。现在让我们计算一下无效的那些:
考虑支柱2n choose n
ins和(n - 1)
的所有组合。所有这些都包括一个点(n + 1)
,其中列车被计为在没有列车的情况下离开支线。我们假设p
在其前面有p
个ins和k
- 那么剩余的ins的数量是(k + 1)
;剩下的出局(n - 1 - k)
。
现在,在 (n + 1) - (k + 1) = (n - k)
之后开始反转这些组合中的每一个组合的输入和输出,以便进入和离开。每个反转的部分都必须{{1} } ins和p
出局。但是现在,如果我们在(n - k)
之前和之后总计输入次数,我们会得到(n - 1 - k)
ins和p
。我们刚刚计算了k + (n - k) = n
ins和(k + 1) + (n - 1 - k) = n
出局无效的组合数。如果我们假设一个这样的组合可能没有被计算,理论上在n
之后反转该组合,你会发现n
ins和p
组合的组合未计算在内。但根据定义,我们已将它们全部计算在内,因此我们假定的额外组合不可能存在。
总有效组合为(n - 1)
,加泰罗尼亚数字。
(改编自汤姆戴维斯的解释:http://mathcircle.berkeley.edu/BMC6/pdf0607/catalan.pdf)
答案 1 :(得分:1)
首先,请注意,您可能会忽略将火车从直接传输到传出的可能性:这样的移动可以通过将火车移动到支线然后再移出来完成。
表示列车从入口移动到支线(
,列车从支线移动到外出)
,你可以在列车的排列和n对正确平衡的列之间得到双射插入语。该陈述需要证明,但证明的唯一困难部分是证明没有两串平衡括号对应于相同的排列。这些字符串的数量是n'Catalan number,或者选择(2n,n)/(n + 1),其中choose(n,k)是从n中选择k项的方式的数量。< / p>
以下是计算解决方案的代码:
def perms(n):
r = 1
for i in xrange(1, n+1):
r *= (n + i)
r //= i
return r // (n + 1)
您可以使用此代码生成所有排列,这也会暴露解决方案的加泰罗尼亚语性质。
def perms(n, i=0):
if n == 0:
yield []
for k in xrange(n):
for s in perms(k, i+1):
for t in perms(n-k-1, i+k+1):
yield s + [i] + t
print list(perms(4))
输出:
[[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3], [0, 2, 3, 1],
[0, 3, 2, 1], [1, 0, 2, 3], [1, 0, 3, 2], [1, 2, 0, 3],
[2, 1, 0, 3], [1, 2, 3, 0], [1, 3, 2, 0], [2, 1, 3, 0],
[2, 3, 1, 0], [3, 2, 1, 0]]
答案 2 :(得分:0)
系统的状态可以通过给出左侧,正弦和右侧轨道中的3个(有序!)发动机列表来描述。鉴于状态,可以计算所有可能的移动。这创建了一个可能性树:树的根是初始状态,并且每个移动对应于导致新状态的分支。分支末尾的最终状态(叶子)是你在正确轨道上的最终位置。
所以,你必须建立和探索所有的树,最后你必须计算所有的叶子。树是一种常见的数据结构。
为了澄清,这种情况下的树不会替换堆栈。堆栈用于存储您的数据(引擎的位置);树用于跟踪算法的进度。每次有状态(树的节点)时,您必须分析数据(=堆栈的内容)并找到可能的移动。每次移动都是算法树中的一个分支,它会导致堆栈的新状态(因为引擎已经移动)。所以基本上你会有一个&#34;配置&#34;树的每个节点的3个堆栈。