我在技术面试论坛中发现了这个谜题,但是没有答案。我能想到的一种方法是找到所有握手的排列,然后检查每个排列是否满足要求。
请问有没有更有效率的解决方案?
@编辑:从评论中澄清:N必须是偶数。
我会尝试一种分治解决方案。如果人1与人x握手,则剩下的人可以分成两组,可以视为坐在两个圆桌上。
n
次查找(以及计算值直到 n
)。 - nneonneo这个解决方案非常简单,以 Python 函数的形式给出(Python 3.3+):
@lru_cache(maxsize=None) # memoize
def num_handshakes(n):
if n % 2 == 1: return 0
elif n == 0: return 1
res = 0
for i in range(0, n, 2):
res += num_handshakes(i) * num_handshakes(n-2-i)
return res
例子:
>>> num_handshakes(8)
14
这基本上实现了@Buhb的分治方法。另一种解决方案是,一旦我们知道答案与卡塔兰数有关:
from math import factorial as fac
def catalan(n):
return fac(2*n) // fac(n+1) // fac(n)
def num_handshakes(n):
if n % 2 == 1: return 0
return catalan(n//2)
f(n) = sum( f(i-2) + f(n-i) for i in range(2, n))
或者在代码中
def f(n):
if n == 0:
# zero people can handshake
return 1
if n == 1:
# it takes two to tango
return 0
ways = 0
# what if person 1 shakes with person i ?
for i in range(2, n+1):
# splits table into independent sets 2 .. i-1 and i+1 .. n
ways += f(i-2) * f(n-i)
return ways
一个奇数人数的群体不能握手,但前几个偶数位置的f值分别为1、2、5、14、42。
在整数序列百科全书中搜索,这看起来像是著名的卡特兰数http://oeis.org/A000108
这些序列真的相同吗?还是只是起始相似?它们是相同的。一本数学书证实了我们定义f的递归关系也适用于卡特兰数 https://en.wikipedia.org/wiki/Catalan_number#Properties。
回溯算法怎么样?
由于您的桌子是圆形(对称),因此您可以通过假设人员0
始终是最上面的握手之一来优化问题。
我认为这可能是一个解决方案,即使 n=2m.
将人们从1到2m编号。
在第 j
轮中,1≤j≤m
,人员 j 与人员 j+1
握手,所有其他握手都“平行”于此(因此,j-1 与 j+2,j-2 与 j+3 等等 - 在整个过程中,必要时标签按模数解释)。在这 m 轮结束时,每个人都与所有离他们奇数距离的人握过手。
在第 m+j 轮中,1≤j≤m
,j 与 j+2 握手,所有其他握手都是平行的(因此 j-1 与 j+3,j-2 与 j+4 等等)。这处理了所有相隔偶数人的情况。因此总共需要 2m 轮。
如问题陈述中所述,2m-1 轮是不可能的,因此答案是 2m。
奇数情况更容易。在第 j 轮中,人员 j 不参与,而 j-1 与 j+1 打招呼,j-2 与 j+2 打招呼,以此类推,同样使用 n 轮。
这里的结果遵循卡特兰数列。以下是我的C++代码:
#include <bits/stdc++.h>
using namespace std;
long c[17] ;
void sieve(){
c[0] = 1;
c[1] = 1;
for(int i =2;i<=15;i++){
for(int j =0;j<i;j++){
c[i] += c[j]*c[i-j-1];
}
}
}
int main(void){
sieve();
int t;
scanf("%d",&t);
while(t--){
int n ;
scanf("%d",&n);
if(n%2!=0){
cout<<"0"<<endl;
}
else{
n = n>>1;
cout<<c[n]<<endl;
}
}
return 0;
}
由于有n个人且每次只有2个人握手,如果假设A与B握手,B又与A握手,我们不能重复计算即AB和BA都不能被计算,这意味着排列不成立,即不是排列而是组合问题...
因此,应用组合公式变为:nC2 => (n*(n-1))/2。
因此,我们可以直接将此公式应用于问题以得到答案。
人们可以用 (n-1)+(n-2)+.....+1
种方式进行握手。这是针对线性的。
圆桌有 n
种方式。
n
为偶数时,解由卡特兰数C(n/2)给出(当n
为奇数时解为0)。参见[OEIS A000108](http://oeis.org/A000108):“在圆上连接2n个点以形成n条不相交弦的方法数”。 - nneonneo