递推递归Word格式.docx
《递推递归Word格式.docx》由会员分享,可在线阅读,更多相关《递推递归Word格式.docx(33页珍藏版)》请在冰豆网上搜索。
【样例】
trave1.intrave1.out
abc1
bca
【算法分析】
在肯定有解的情况下,上述算法最终可以递归调用到0、1个结点,如果有多组解,那么调用到两个结点时,如先序为ab、后序为ba,此时有可能有如下两种结构:
aa
/\
bb
这两种结构的中序遍历结果分别为:
ba、ab,有两种。
根据分步相乘的原理,对比两个字符串,每出现一次如上的情况,可能有的结构数目(结构不同,中序遍历结果也不同,因此可能有的二叉树结构的数目就是可能有的中序遍历结果数目)就乘以2一次,最终得到总的数目。
这也可以理解为一种递推的方法。
从这里可以看到,在肯定有解的情况下,给定先序遍历的结果和后序遍历的结果,可能有2n种可能的结构,也就是中序遍历可能得到2n种不同的结果,其中n>
=0。
那么这里的n最大可能是多少呢?
可以证明n的最大值为字符串的长度加1整除2。
【参考程序】
programfgdjfk;
vari,j,m,sum:
longint;
s1,s2:
string;
begin
assign(input,'
travel.in'
);
reset(input);
assign(output,'
travel.out'
rewrite(output);
readln(s1);
readln(s2);
sum:
=1;
fori:
=1tolength(s1)-1do
begin
m:
=pos(s1[i],s2);
ifm>
1then
ifs1[i+1]=s2[m-1]then
=sum*2;
end;
writeln(sum);
close(input);
close(output);
end.
2产生数
源程序名build.?
可执行文件名build.exe
输入文件名build.in
输出文件名build.out
给出一个整数n(n<
1030)和m个变换规则(m≤20)。
约定:
一个数字可以变换成另一个数字,规则的右部不能为零,即零不能由另一个数字变换而成。
而这里所说的一个数字就是指一个一位数。
现在给出一个整数n和m个规则,要你求出对n的每一位数字经过任意次的变换(0次或多次),能产生出多少个不同的整数。
共m+2行,第一行是一个不超过30位的整数n,第2行是一个正整数m,接下来的m行是m个变换规则,每一规则是两个数字x、y,中间用一个空格间隔,表示x可以变换成y。
仅一行,表示可以产生的不同整数的个数。
build.inbuild.out
1236
2
12
23
如果本题用搜索,搜索的范围会很大(因为n可能有30位!
),显然无法在规定的时间内出解。
而我们注意到本题只需计数而不需要求出具体方案,所以我们稍加分析就会发现,可以用乘法原理直接进行计数。
设F[i]表示从数字i出发可以变换成的数字个数(这里的变换可以是直接变换,也可以是间接变换,比如样例中的1可以变换成2,而2又可以变换成3,所以1也可以变换成3;
另外自己本身不变换也是一种情况)。
那么对于一个长度为m位的整数a,根据乘法原理,能产生的不同的整数的个数为:
F[a[1]]*F[a[2]]*F[a[3]]*…*F[a[m]]。
下面的问题是如何求F[i]呢?
由于这些变换规则都是反映的数字与数字之间的关系,所以定义一个布尔型的二维数组g[0..9,0..9]来表示每对数字之间是否可以变换,初始时都为False;
根据输入的数据,如果数字i能直接变换成数字j,那么g[i,j]置为True,这是通过一次变换就能得到的;
接下来考虑那些间接变换可得到的数字对,很明显:
如果i可以变为k,k又可以变为j,那么i就可以变为j,即:
fork:
=0to9do
forj:
g[i,j]=g[i,j]or(g[i,k]andg[k,j]);
最后还要注意,当n很大时,解的个数很大,所以要用高精度运算。
programdrgjol;
vari,j,k,n,m,l:
a:
array[1..30]oflongint;
//读入的数
f:
array[0..9]oflongint;
//i对应的变换数目
g:
array[0..9,0..9]ofboolean;
//变换规则
array[1..1000]oflongint;
//储存答案
ch:
char;
proceduremul(k:
longint);
//计算sum
vari,x:
x:
=0;
=1toldo
sum[i]:
=sum[i]*k+x;
=sum[i]div10;
=sum[i]mod10;
whilex<
>
0do
inc(l);
sum[l]:
=xmod10;
=xdiv10;
end;
{mul}
build.in'
build.out'
n:
fillchar(g,sizeof(g),false);
whilenotseekeolndo
inc(n);
read(ch);
a[n]:
=ord(ch)-ord('
0'
readln(m);
=1tomdo
readln(j,k);
g[j,k]:
=true;
g[i,j]:
=g[i,j]or(g[i,k]andg[k,j]);
//计算变换规则
fillchar(f,sizeof(f),0);
=0to9dog[i,i]:
ifg[i,j]theninc(f[i]);
//统计数目
sum[1]:
l:
=1tondo//求sum
mul(f[a[i]]);
=ldownto1do//输出
write(sum[i]);
writeln;
3出栈序列统计
源程序名stack.?
可执行文件名stack.exe
输入文件名stack.in
输出文件名stack.out
栈是常用的一种数据结构,有n个元素在栈顶端一侧等待进栈,栈顶端另一侧是出栈序列。
你已经知道栈的操作有两种:
push和pop,前者是将一个元素进栈,后者是将栈顶元素弹出。
现在要使用这两种操作,由一个操作序列可以得到一系列的输出序列。
请你编程求出对于给定的n,计算并输出由操作数序列1,2,…,n,经过一系列操作可能得到的输出序列总数。
【输入】【输出】
就一个数n(1≤n≤1000)。
一个数,即可能输出序列的总数目。
stack.instack.out
35
在第一章练习里,我们通过回溯的方法计算并输出不同的出栈序列,这里只要求输出不同的出栈序列总数目,所以我们希望能找出相应的递推公式进行处理。
从排列组合的数学知识可以对此类问题加以解决。
我们先对n个元素在出栈前可能的位置进行分析,它们有n个等待进栈的位置,全部进栈后在栈里也占n个位置,也就是说n个元素在出栈前最多可能分布在2*n位置上。
出栈序列其实是从这2n个位置上选择n个位置进行组合,根据组合的原理,从2n个位置选n个,有C(2n,n)个。
但是这里不同的是有许多情况是重复的,每次始终有n个连续的空位置,n个连续的空位置在2n个位置里有n+1种,所以重复了n+1次。
所以出栈序列的种类数目为:
C(2n,n)/(n+1)=2n*(2n-1)*(2n-2)…*(n+1)/n!
/(n+1)=2n*(2n-1)*(2n-2)*…*(n+2)/n!
。
考虑到这个数据可能比较大,所以用高精度运算来计算这个结果。
本题实际是一个经典的Catalan数模型。
有关Catalan数的详细解释请参考《组合数学》等书。
programdfgdjk;
vari,j,n,m,l:
p:
array[2..2000]ofboolean;
//素数表
array[1..2000]oflongint;
a,b,c:
//素因子表
procedureprime;
//计算1~2000内的素数
vari,j:
fillchar(p,sizeof(p),true);
i:
=2;
whilei*i<
=2000do
j:
=i+i;
whilej<
p[j]:
=false;
=j+i;
inc(i);
=2to2000do
ifp[i]then
inc(m);
a[m]:
=i;
{prime}
//高精乘法求sum