主题:[讨论]谁来帮帮我
Mato完整版
[专家分:1270] 发布于 2008-07-01 18:56:00
有一种被编上了号的三叉树由以下方法构成:
(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
最后更新于:2008-07-01 19:07:00
回复列表 (共2个回复)
沙发
Mato完整版 [专家分:1270] 发布于 2008-07-03 11:47:00
我找到算法了。
很容易知道,如果求出了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的范围了)。
板凳
Mato完整版 [专家分:1270] 发布于 2008-07-03 11:50:00
程序:
{$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里,帖子先锁上了。
我来回复