题目源自于阿里巴巴校招笔试题。
题目:12个高矮不同的人,排成2排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多 少种?
思路:假设12个人的身高为,0 1 2 3 4 5 6 7 8 9 10 11。
我们先把这12个人从低到高排列,然后,选择6个人排在第一排,那么剩下的6个肯定是在第二排。
用0表示对应的人在第一排,用1表示对应的人在第二排。
那么含有6个0,6个1的序列,就对应一种方案。
比如000000111111就对应着
第一排:0 1 2 3 4 5
第二排:6 7 8 9 10 11
010101010101就对应着
第一排:0 2 4 6 8 10
第二排:1 3 5 7 9 11
问题转换为,可以怎么样安排这些0/1?
我们来观察,什么样的序列符合要求。
对于某一个1的出现,观察在这个1之前的这些数(比当前的1所对应的人个子小的人),所对应的这些人:要么是在当前 这个1的人同一排左边(即为1),要么是在第一排(即为0)。
题目:12个高矮不同的人,排成2排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多 少种?
思路:假设12个人的身高为,0 1 2 3 4 5 6 7 8 9 10 11。
我们先把这12个人从低到高排列,然后,选择6个人排在第一排,那么剩下的6个肯定是在第二排。
用0表示对应的人在第一排,用1表示对应的人在第二排。
那么含有6个0,6个1的序列,就对应一种方案。
比如000000111111就对应着
第一排:0 1 2 3 4 5
第二排:6 7 8 9 10 11
010101010101就对应着
第一排:0 2 4 6 8 10
第二排:1 3 5 7 9 11
问题转换为,可以怎么样安排这些0/1?
我们来观察,什么样的序列符合要求。
对于某一个1的出现,观察在这个1之前的这些数(比当前的1所对应的人个子小的人),所对应的这些人:要么是在当前 这个1的人同一排左边(即为1),要么是在第一排(即为0)。
而且,肯定要有一个0的(在当前这个1的人的前正方)。
那么,对于当前这个1之前的数,我们的要求就是:0的个数大于1的个数。
由此看出这是一个由小到大的递推问题,另外很明显这是个排列组合问题,考虑使用递归来寻找可能的状态空间。对于序列从小开始逐步扩大子空间,当序列到达所求长度时递归结束。
代码:
#include <stdio.h>
int num = 0;
const int N = 12;
int list[N];
void printArray(int data[])
{
int i;
for(i=0;i<N;i++)
printf("%d ",data[i]);
printf("\n");
}
void ProduceArray(int data[], int index, int n_0)
{
if(index < N) //组合序列长度没到达
{
//0的个数不能超过总数的一般
if(n_0 < N/2)
{
data[index] = 0;
ProduceArray(data, index+1, n_0 + 1);
}
//0的个数不能超过总数的一般
//并且0的个数要大于1的个数
if((index - n_0 < N/2)&&(index - n_0 < n_0))
{
data[index] = 1;
ProduceArray(data, index+1, n_0);
}
}
else
{
printArray(data);
num++;
}
}
void fun()
{
int i;
list[0] = 0;
ProduceArray(list, 1, 1);
}
int main()
{
fun();
printf("%d\n",num);
return 1;
}
问题等价:
换个角度去思考这个问题。这里要求这个序列从左到右,或者说序列的长度从小到大,都需要满足0的个数大于1的个数。那么可以联想到栈,把0看成入栈操作,把1看成出栈操作,如果1的个数多余0的个数则会出错。现在就是在合理的安排6个0和6个1,使得能够完成出入栈。于是问题就等价为求6个元素构成合法的入栈出栈序列有多少个?
这就是catalan数,这里只是用于栈,等价地描述还有,二叉树的枚举,多边形分成三角形的个数,圆括弧插入公式中的
方法数,其通项是c(2n, n)/(n+1)。