ZOJ-1062-Trees Made to Order

被这到题小郁闷了一下,终于AC了,郁闷人的要点说到底是数数的问题,从哪数,数到哪,算几? 

AC率高的有两种,一种是真的太简单,sample数据覆盖广,另一种是这道题,好不容易AC了,赶紧再多来几遍看看能不能少用些时间或memory。1062高达63%的AC率,但却是道稍有难度的题,主要是中间算每段的起始和子树的节点数和入口编号有些琐碎。

题目算法

先说明一下,关于递归函数的参数,我也在网上搜到看了别人的程序,有使用单独一个参数就是编号本身的,这个当然很好,尤其是看起来程序非常清晰,不过就是能得出子树的编号,说明也已经得到它的节点数目m以及它是有m个节点的树中的第几个。。。所以干脆还是传节点数目m和它在m个节点的树中的名次。

废话不说了,算法如下:

1。首先计算出所有含有m个节点的树的总数目,存在idex[m]里,这一步我是先写过程序算出按照题目的要求,节点数不会超过18,并且手动把结果存在程序里了。idex[0]=1。这个程序应该很好些的,就不详细说了。

2。算出输入数字的节点数目及其名次,调用递归函数void gettree(int node, long bit).

3。在递归函数中,从i=0开始,逐次执行temp -= idex[i] * idex[node-1-i];,直到temp<=0,这时恢复temp最后一次大于0的值。

讲到这里,先给个例子,比如题中有图的20,那么20-1-2-5=12,这时再减14就为负所以20有4个节点,除去根节点,还剩3个节点,3个节点分配顺序由小到大为:

0   3---idex[0]*idex[3] = 1*5 = 5
1   2---idex[1]*idex[2] = 1*2 = 2
2   1---idex[2]*idex[1] = 2*1 = 2
3   0---idex[3]*idex[0] = 1*2 = 5

这时12-5=7,7-2=5,5-3=2,2-5<0,所以这3个节点的分配为左3右0。

4。知道了左右子树的节点分配,也就知道了左右子树各能产生多少种可能,利用乘法原理。

这时剩下的数字代表第几位。。。所以减1然后处以右子树的可能组合数idex[node-1-i],又因为除下来的数又是从0开始数,所以再加1,于是就是:

x = ((temp-1)/idex[node-1-i])+1,这是左子树在节点为i个时的入口编号,接下来求得最后剩余的数字,

y = temp-(x-1)*idex[node-1-i];,这是右子树在节点为node-1-i个时的入口编号。

(这里的node指得是当前整个树的节点数目)

5。接下来调整相应的输出格式,递归,结束条件是node==1,输出X。

程序清单如下

// C++ 00:00.00 432K 
#include < stdio.h >

long  idex[ 19 ];
long   in ;

void  gettree( int  node,  long  bit)
{
    
if ( 1 == node)
    {
        printf(
" X " );
    }
    
else
    {
        
long  temp = bit;
        
int  i;
        
for (i = 0 ;i < node;i ++ )
        {
            temp 
-=  idex[i]  *  idex[node - 1 - i];
            
if (temp <= 0 )
                
break ;
        }
        temp 
+=  idex[i]  *  idex[node - 1 - i];
        
long  x,y;
        x 
=  ((temp - 1 ) / idex[node - 1 - i]) + 1 ;
        y 
=  temp - (x - 1 ) * idex[node - 1 - i];
        
if ((node - 1 - i > 0 ) && (i > 0 ))
        {
            printf(
" ( " );
            gettree(i,x);
            printf(
" )X( " );
            gettree(node
- 1 - i,y);
            printf(
" ) " );
        }
        
else   if (i > 0 )
        {
            printf(
" ( " );
            gettree(i,x);
            printf(
" )X " );
        }
        
else
        {
            printf(
" X( " );
            gettree(node
- 1 - i,y);
            printf(
" ) " );
        }
    }
}

void  solve()
{
    
long  temp = in ;
    
int  i;    
    
for (i = 1 ;;i ++ )
    {
        temp 
-=   idex[i];
        
if (temp <= 0 )
            
break ;
    }
    temp 
+=  idex[i];
    gettree(i,temp);
    printf(
"/n " );
}

int  main()
{
//     freopen("1062.txt","r",stdin);
    idex[ 0 =   1 ;
    idex[
1 =   1 ;
    idex[
2 =   2 ;
    idex[
3 =   5 ;
    idex[
4 =   14 ;
    idex[
5 =   42 ;
    idex[
6 =   132 ;
    idex[
7 =   429 ;
    idex[
8 =   1430 ;
    idex[
9 =   4862 ;
    idex[
10 =   16796 ;
    idex[
11 =   58786 ;
    idex[
12 =   208012 ;
    idex[
13 =   742900 ;
    idex[
14 =   2674440 ;
    idex[
15 =   9694845 ;
    idex[
16 =   35357670 ;
    idex[
17 =   129644790 ;
    idex[
18 =   477638700 ;
    
while (scanf( " %ld " & in ) != EOF  &&   in > 0 )
        solve();
//     fclose(stdin);
     return   0 ;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值