ZOJ-1062-Trees Made to Order

本文详细介绍了一种解决编号为1062的算法题的方法,重点在于如何通过递归函数计算特定节点数目下的树的总数,以及如何确定每个节点的具体位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

被这到题小郁闷了一下,终于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、付费专栏及课程。

余额充值