回 帖 发 新 帖 刷新版面

主题:[讨论]谁来帮帮我

有一种被编上了号的三叉树由以下方法构成:
(1)i号三叉树只有根结点     (i=0或1或2)
(2)i号三叉树根结点的第一棵子树是i-3号,第二棵子树是i-2号,第三颗子树是i-1号 (i>2)
   现把这种三叉树按以下顺序遍历:
(1)访问根结点。
(2)如果根结点有第二棵子树,遍历第二棵子树。
(3)如果根结点有第一棵子树,遍历第一棵子树。
(4)如果根结点有第三棵子树,遍历第三棵子树。
   按照以上遍历方法,三叉树内第x个访问到的结点值为x。

   给出i号三叉树内的任意两个结点的值,求从第一个结点走到第二个结点,最少要经过多少条边?怎么走?
[输入]
三个数,i、m、n,表示i号三叉树上值为m和n的结点。(3<=i<=15)
m、n都不超过i号三叉树上的总结点数,并且m<>n。
[输出]
第一行一个数,表示最少经过的边数k。
第二行k个数字,表示走法,每个数字为0、1、2、3中的一个,分别表示:
0:从一个结点走到它的前趋结点;
1:从一个结点走到它的第一个子结点。
2:从一个结点走到它的第二个子结点。
3:从一个结点走到它的第三个子结点。
数字之间不空格。
[输入样例]
5 4 12
[输出样例]
5
00331

回复列表 (共2个回复)

沙发

我找到算法了。

很容易知道,如果求出了i号三叉树的根结点到值为m、n的两个结点的最短路径,则只要把两个最短路径前面的相同路径去掉,再把第一条路径中的字符全变成0并加上第二条路径,就是两个结点间的最短路径了。
所以本题的关键是,如何求根结点到m、n两个结点的最短路径?
显然,如果要构造这个三叉树,再遍历,会浪费很多时间。我们可以知道,如果把i(i>2)号三叉树的遍历结果写出来,则结果分四段:
    (1)根结点。
    (2)第二棵子树的遍历结果,就是i-2号。
    (3)第一棵子树的遍历结果,就是i-3号。
    (4)第三棵子树的遍历结果,就是i-1号。
并且顺序是(1)-->(2)-->(3)-->(4)。
    如果把遍历结果用字符串存放的话,本题i的最大值为15,我计算过了,15号三叉树的结点有6000多个,字符串肯定是存放不下的,况且我还把本题i的最大值改了一下,原题中,对i没有限制,只是说三叉树的节点数不超过MAXLONGINT。
    有没有更好的算法呢?
    我们可以发现结点总数的规律:将i号三叉树的总结点数记为D(i),则:
           D(i) = 1                                            (0<=i<=2)
           D(i) = D(i - 3) + D(i - 2) + D(i - 1) + 1           (i>2)
    假设nnn是i号三叉树中值为x的结点,则:
    (1)若x=1,则nnn是根结点。
    (2)若1<x<=(D(i - 2) + 1),则nnn是第二棵子树中的结点。
    (3)若(D(i - 2) + 1)<x<=(D(i - 2) + D(i - 3) + 1),则nnn是第一棵子树中的结点。
    (4)若(D(i - 2) + D(i - 3) + 1)<x,则nnn是第三棵子树中的结点。

    根据以上规律,算法就出来了:先按x值确定结点的位置(根结点或在哪个子树中),如果不是根结点,递归处理它所在的子树,直到子树的根结点的三棵子树都只有根结点(就是3号三叉树)或者到一棵子树的根结点为止,就获得了从根结点到该结点的最短路径。
    如果总结点数超过MAXLONGINT,那么TP就无能为力了,需要FP的int64(当然,我连FP的界面是什么都弄不清楚,更不知道int64的范围了)。

板凳

程序:
{$N+}
CONST NNNNN = 35;
TYPE
   {Integer type declare}
   I_ = INTEGER;
   SI_ = SHORTINT;
   LI_ = LONGINT;
   BI_ = BYTE;
   WI_ = WORD;
   {Real type declare}
   R_ = REAL;
   SR_ = SINGLE;
   DR_ = DOUBLE;
   ER_ = EXTENDED;
   CR_ = COMP;
   {Other type declare}
   C_ = CHAR;
   B_ = BOOLEAN;
   S_ = STRING;

   a1_ = 0..NNNNN;
VAR
   d: ARRAY[a1_] OF LI_;
   i: a1_;
   m, n: LI_;
   s, s1, s2, fr: S_;
PROCEDURE inputfile;
VAR
   fi: TEXT; infl: S_;
BEGIN
    WRITE('Input file name: ');
    READLN(infl);
    ASSIGN(fi, infl);
    RESET(fi);
    READ(fi, i, m, n);
    CLOSE(fi);
END;
PROCEDURE rrr(xx: LI_; ii: a1_);
VAR
   p1, p2: LI_;
BEGIN
    IF xx = 1 THEN EXIT;
    p1 := d[ii - 2] + 1;
    p2 := d[ii - 3] + p1;
    IF ii = 3 THEN BEGIN
       IF xx = 2 THEN s := s + '2';
       IF xx = 3 THEN s := s + '1';
       IF xx = 4 THEN s := s + '3';
       EXIT;
    END ELSE
           IF xx <= p1 THEN BEGIN
              s := s + '2'; rrr(xx - 1, ii - 2);
           END ELSE
                  IF xx <= p2 THEN BEGIN
                     s := s + '1'; rrr(xx - p1, ii - 3);
                  END ELSE BEGIN
                     s := s + '3'; rrr(xx - p2, ii - 1);
                  END
END;
PROCEDURE nums;
VAR
   k1, k2, k3, k4, sum: LI_;
BEGIN
    d[0] := 1; d[1] := 1; d[2] := 1;
    k2 := 1; k3 := 1; k4 := 1;
    FOR k1:=3 TO i DO BEGIN
        sum := k2 + k3 + k4 + 1;
        d[k1] := sum;
        k2 := k3;
        k3 := k4;
        k4 := sum;
    END;
END;
PROCEDURE printfile;
VAR
   fo: TEXT; outfl: S_;
BEGIN
    WRITE('Output file name: ');
    READLN(outfl);
    ASSIGN(fo, outfl);
    REWRITE(fo);
    WRITE(fo, fr);
    CLOSE(fo);
END;
VAR
   j: I_;
BEGIN
    inputfile;
    nums;
    rrr(m, i);
    s1 := s;
    s := '';
    rrr(n, i);
    s2 := s;
    WHILE (s1[1] = s2[1]) AND (s1 <> '') DO BEGIN
          s1 := COPY(s1, 2, 255);
          s2 := COPY(s2, 2, 255);
    END;
    IF s1 = '' THEN
       fr := s2
    ELSE BEGIN
       FOR j:=1 TO LENGTH(s1) DO fr := fr + '0';
       fr := fr + s2;
    END;
    printfile;
END.

大家如有更好的算法,请发到我的邮箱Matodied@yeah.net里,帖子先锁上了。

我来回复

您尚未登录,请登录后再回复。点此登录或注册