POJ1095 Trees Made to Order(JAVA)
这题用到了卡特兰数,比较麻烦。关于卡特兰数的基本概念百度一下你就知道。
使用卡特兰数对数组元素进行分组之后,需要具体计算一下要求的是第几组的第几个数,然后向下递归。
首先来看利用卡特兰数分组:
从1开始前4个卡特兰数是 C[1]=1, C[2]=2, C[3]=5, C[4]=14 (C[0]也是有定义的,C[0]=1)
于是我们把第1个元素归为第1组,第2,3个元素归为第2组,4,5,6,7,8归为第3组,9到22归为第4组,这样分组就完成了
这里的每个元素,就对应着题目里的每一棵树。而卡特兰数的下标,即树的节点数
所以1个节点的树有1棵,2个节点的树2棵,3个节点的树5棵,4个节点的树14棵
比如现在我们求第17棵树在第几组,17-C[1]-C[2]-C[3]=9,因为9<=C[4],所以第17棵树肯定在4个节点的树的第9个位置
然后是对卡特兰数进行递归拆分:
由卡特兰数的性质,C[i]=C[0]*C[i-1] + C[1]*C[i-2] + ... + C[i-2]*C[1] + C[i-1]C[0]
我们找到所在组的位置后,就要寻找向下递归的参数。
比如我们刚才找到了第17棵树在C[4]的第9个位置,接下来要找这第9个位置的树究竟是什么
首先,C[4]=C[0]*C[3] + C[1]*C[2]+C[2]*C[1] +C[3]*C[0],这相当于把C[4]所含的14棵树再一次进行了分组
它使用规模更小的卡特兰数的乘积,对原先的卡特兰数进行分组,把C[4]分成C[0]*C[3]=5,C[1]*C[2]=2,C[2]*C[1]=2,C[3]*C[0]=5,一共4组
那么第9棵树在哪呢?9-C[0]*C[3]-C[1]*C[2]=2,因为2<=C[2]*C[1],所以这第9棵树肯定在C[2]*C[1]那个分组里的第2棵
C[2]就是左子树,由2个树节点构成。C[1]是右子树,由1个树节点构成。
接下来应该对左右子树进行递归。递归结束条件是C[1],根据题意,找到C[1],就可以输出一个X
但现在我们还缺少一些递归参数,右子树C[1]没问题,反正就输出X。但右子树是由2个节点构成的树,C[2]=2,所以这样的树有2棵,究竟是哪一棵?
这里需要停下来想一想,对于这种卡特兰数形式的树来说,左子树x个节点,右子树y个节点,其内涵是什么?
- 首先这是一棵由x+y+1个节点构成的树,对吧?
- 其次这棵树一共有几种变化?C[x]*C[y]种
- 比如x=2,y=3,那么这棵树一共有C[2]*C[3]=2*5=10种变化,具体来看:
- 3个节点的右子树有5种变化,每当右子树5种变化结束之后,左子树才变化一次,我们把这样叫一轮变化。一共有C[2]=2轮,每轮5种,共2*5=10种变化
- 假设我们要找这10种变化里的第6种变化,那么是第几轮的第几种变化?应该是第二轮的第一种,对吧?
- 如何用公式来表示呢?
- 我们相当于用右子树的C[3]=5把10种变化分成了2轮,那么求第n种变化是第几轮第几种,公式为:第(n-1)/C[3]+1=轮的第(n-1)%C[3]+1种变化
- 当n=6,(n-1)/C[3]+1=2,(n-1)%C[3]+1=1,即第2轮第1种
- 即左子树C[2]里面的第2棵树,右子树C[3]里的第1棵树
好了,这样我们已经找齐所有的递归参数,可以对左右子树分别进行递归。
贴一下AC代码,JAVA版的:
import java.util.Scanner; public class POJ1095 { static long[] catalan = new long[30]; static void initCatalan(){ catalan[0]=catalan[1]=1; for(int i=2;i<catalan.length;i++){ catalan[i] = (4*i-2)*catalan[i-1]/(i+1); } } // n个节点的第k种情况 static void draw(int n,long k){ if(n==0) return; if(n ==1){ System.out.print("X"); return; } // cn:去掉根节点后的节点数 int cn=n-1,leftn=0,rightn=0; long rightc=0,leftk=0,rightk=0; for(int i=cn;i>=0;i--){ long j = catalan[i]*catalan[cn-i]; if(j<k){ k-=j; }else { leftn = cn-i; rightn = i; rightc = catalan[i]; break; } } leftk=(k-1)/rightc +1; rightk=(k-1)%rightc +1; if(leftn>0) { System.out.print("("); draw(leftn, leftk); System.out.print(")"); } System.out.print("X"); if(rightn>0) { System.out.print("("); draw(rightn, rightk); System.out.print(")"); } } public static void main(String[] args) { initCatalan(); Scanner sc = new Scanner(System.in); long c = Integer.valueOf(sc.nextLine()); int i; while (c!=0) { for(i=1;i<catalan.length;i++){ if(catalan[i]<c){ c-=catalan[i]; }else break; } draw(i,c); System.out.println(); c = Integer.valueOf(sc.nextLine()); } } }