《数据结构》上有一个题目,问 4 节火车,进入一个岔道的出岔道的组合有多少种,我当时一看就不想做,如果有电脑在旁边,一个深搜就好了,可惜,没有,所以认认真真写了半天,才得到 14 种这个答案。
今天哈尔滨网赛前,无聊,研究一下,用半个小时写了一下这个深搜:
#include <stdio.h>
#include <stack>
#define N 25
using namespace std;
int res[N],pos,cnt,MaxN;
stack<int> st;
void test(int x){
int i;
if(pos==MaxN){//已经全部出去了,打印结果
cnt++;
printf("%5d:",cnt);
for(i=0;i<pos-1;i++)
printf("%3d",res[i]);
printf("%3dn",res[i]);
return;
}
//对于每一个新元素入栈,我们决策一下它前面的元素什么时候出栈
if(!st.empty()){
//一个个测试,当前栈顶的出去了
res[pos++]=st.top();
st.pop();
//下一次递归再同样决策
test(x);
//递归返回了,把出去的拉回来
st.push(res[--pos]);
}
if(x>MaxN)return;//大于MaxN的不考虑了
//当前要决策的元素,可以加到栈里面了
st.push(x);
//决策下一个元素
test(x+1);
//状态还原,刚刚放进去的出来,好返回上一个数字继续找
st.pop();
}
int main(){
while(scanf("%d",&MaxN)!=EOF){
if(MaxN<=N){
pos=0;
cnt=0;
test(1);
}else{
printf("Max Input = %dn",N);
}
}
return 0;
}
如书上题目要求的,MaxN=4 时的结果是:
4
1: 1 2 3 4
2: 1 2 4 3
3: 1 3 2 4
4: 1 3 4 2
5: 1 4 3 2
6: 2 1 3 4
7: 2 1 4 3
8: 2 3 1 4
9: 2 3 4 1
10: 2 4 3 1
11: 3 2 1 4
12: 3 2 4 1
13: 3 4 2 1
14: 4 3 2 1
再输出几个结果,发现出栈数目是爆炸增长的,再草稿纸上写了递推的关系,我是分 1 开始为开始元素,2 开始为开始元素,3 为开始元素……这样讨论的,分别是:f(n-1),f(n-1),f(n-2)+c(1,n-3)……反正,好复杂,我也不想去整理了,于是把输出的前 7 项“1 2 5 14 42 132 429”Google 之,发现这个数列是一个叫卡特兰的数列,看上去,应用还挺多的。在维基百科上,看到这个通项公式:
看上去就有点傻眼了,呃,我自认我不会推导出来~诶诶,数学没学好啊~
引用维基百科上的:
前几项为 (OEIS中的数列A000108): 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...
哇咔咔,数据好大~