pascal递归算法noip竞赛材料.docx
- 文档编号:11210313
- 上传时间:2023-02-25
- 格式:DOCX
- 页数:29
- 大小:29.42KB
pascal递归算法noip竞赛材料.docx
《pascal递归算法noip竞赛材料.docx》由会员分享,可在线阅读,更多相关《pascal递归算法noip竞赛材料.docx(29页珍藏版)》请在冰豆网上搜索。
pascal递归算法noip竞赛材料
信息学竞赛―――递归算法
一个过程(或函数)直接或间接调用自己本身,这种过程(或函数)叫递归过程(或函数).递归程序包含递归和递推两个过程,这两个过程又都是根据一个递推公式进行的。
一般来说,能够用递归解决的问题应该满足以下三个条件
①需要解决的问题可以化为一个或多个子问题来求解,而这些子问题的求解方法与原来的问题完全相同,只是在数量规模上不同;
②递归调用的次数必须是有限的;
③必须有结束递归的条件(边界条件)来终止递归。
例1、楼梯共有N阶台阶,上楼可以以一步上一个台阶,也可以一步上二个台阶。
编一个程序计算上N阶台阶,共有多少种走法?
programstair(input,output);
vars,n:
integer;
functionf(n:
integer):
integer;
begin ifn<3thenf:
=n
elsef:
=f(n-1)+f(n-2);
end;
begin
readln(n);
s:
=f(n);
writeln('s=',s);
end.
例2、骨牌铺法
有1*n的一个长方形,用一个1*1、1*2、1*3的骨牌铺满方格。
例如当n=3时为1*3的方格。
此时用1*1,1*2,1*3的骨牌铺满方格,共有四种铺法。
图4.4.3列出了四种铺法。
●
●
●
●
●
●
●
●
●
输入n(0<=n<=30) 输出 铺法总数
分析:
这道题可以采用猜测法,从具体的n=1,2,3,......开始,列举出结果,根据列举的部分结果进行猜测,推导出公式。
这个猜测推导过程留给读者完成。
问题是:
这种方法中“猜”和“凑”的成分比较多,容易出错。
我们不妨采用组合数学常用的待定系数进行归纳和推导。
设推导公式如下:
f(n)=a*f(n-1)+b*f(n-2)+c*f(n-3)+d*f(n-4)+....(a,b,c,d...是常系数)
即1*n的长方形的铺法由全部(a种)1*(n-1)的长方形铺法总数加上全部(b种)1*(n-2)的长方形的铺法总数加上全部(c种)1*(n-3)的长方形的铺法总数......注意排除重复情况。
(1)将n格分成1格和n-1格,计算f(n-1)的系数a。
右端1格的铺法有一种(a)。
显然,在一格中只有一种铺法,即f(n-1)的系数a=1。
●
●
(2)将n格分成2格和n-2格,计算f(n-2)的系数b。
右端2格的铺法有两种(b)。
由图可见,(b)的铺法包含在(a)的铺法中,而(c)的铺法不同于(a),因此f(n-2)的系数b=1。
(3)将n格分成3格和n-3格,计算f(n-3)的系数c。
右端3格的铺法有两种(图4.4.5)。
由图可见,(d)(e)(f)的铺法都可以归结到1格或2格中去,只有1*3的铺法(g)属于新的,因此f(n-3)的系数c=1。
将n格分成n-x格和x格(4<=x<=n-4)的情况都是重复的,因此不再讨论。
由此得出:
f
(1)=1,f
(2)=2,f(3)=4,
f(n)=f(n-1)+f(n-2)+f(n-3) (n>=5)
程序:
varn:
integer;
functionf(i:
integer):
longint;
begin
ifiin[1..2]
thenf:
=i
elseifi=3
thenf:
=4
elsef:
=f(I-1)+f(I-2)+f(I-3);
end;
begin
readln(n);writeln(f(n));
end.
“铺砖问题”有推广价值。
例如某人走n级的楼梯,每步可以走1级、2级或3级,走完n级楼梯共有多少种走法。
这个问题的数学意义和解法与“铺砖问题”相同。
例3:
划分问题
设s是一个具有n个元素的集合s={a1,a2,…an},现将s集合划分成k个满足下列条件的子集合s1,s2,s3。
。
。
。
;
1、si<>空;
2、si∩sj=空;
3、s1∪s2∪s3….∪sn=s (1<=i,j<=k,i<>j)
则称s1,s2…sn是集合s的一个划分,它相当于把集合s中的n个元素放入k个无标号的盒子中,使得没有一个盒子为空,试确定n个元素的集合放入k个无标号盒的划分数s(n,k)
【算法分析】:
例如S={1,2,3,4},k=3。
细心的读者稍加分析后,不难得出S有6种不同的划分方案,即划分数为6。
其方案为{1,2}∪{3}∪{4} {1,3}∪{2}∪{4} {1,4}∪{2}∪{3} {2,3}∪{1}∪{4} {2,4}∪{1}∪{3} {3,4}∪{1}∪{2}
如果对于任意的S集合和k值,就不能凭籍直觉和经验计算划分数和枚举划分方案了。
必须总结出一个数学规律:
设n个元素a1…an放入k个无标号盒的划分数为S(n,k)。
在配置过程中,
有两种情况:
1.设{an}是k个子集中的一个子集,于是把{a1…an-1}划分为k-1子集有S(n-1,k-1)个划分数;
2.如果{an}不是k个子集中的一个,即an必与其它的元素构成一个子集。
首先把{a1,…,an-1}划分成k个子集,这共有S(n-1,k)种划分方式。
然后 再把an加入到k个子集中的一个子集中去,这有k种加入方式。
对于每一种加入方式,都使集合划分为k个子集,因此由乘法原理知,划分数共有k·s(n-1,k)。
从上面的两种情况分析得出 S(n,k)=S(n-1,k-1)+k·S(n-1,k) (n>1,k≥1)
下面,我们来确定s(n,k)的边界条件:
1.我们不可能把n个元素不放进任何一个集合中去,即s(n,0)=0;也不可能在不允许空盒的情况下把n个元素放进多于n的k个集合中去,即k>n时S(n,k)=0。
2.把n个元素放进一个集合或把n个元素放进n个集合,方式数显然是1。
即
S(n,1)=1
S(n,n)=1
显然,通过上述分析可得出划分数S(n,k)的递归关系式:
S(n,k)=S(n-1,k-1)+k·S(n-1,k) (n〉k,k≥1)
S(n,k)=0 (n S(n,k)=1 (k=1)或(k=n) 按照划分数S(n,k)的递归定义,可以直接写出它的递归函数S(n,k) functionS(n,k: integer)ofword; begin if(k=0)or(k>n)then s: =0 elseif(k=1)or(k=n)then s: =1 else s: =s(n-1,k-1)+k*s(n-1,k); end; 相似题: m个相同的球放进n个相同的盒子(可以一个盒子放多个)有多少种不同的放法? m个相同的球放进n个不相同的盒子(可以一个盒子放多个)有多少种不同的放法? 例4、简单的背包问题。 设有一个背包,可以放入的重量为s。 现有n件物品,重量分别为t1,t2,t3…ti…tn,ti(1≤i≤n),均为正整数。 从n件物品中挑选若干件,使得放入背包的重量之和正好为s 【输入样例】 【输出样例】 thenumberofobject: 5 number: 1weight: 1 totalweight=10 number: 3weight: 2 16275 number: 4weight: 7 【分析】尝试构造函数snap(s,n)解决本题,分析情况如下: ①、先取最后一个物品tn放入背包中,若tn=s,刚好放入包中,问题得到解决并输出(n,tn). ②、若tn>s,则不能放入包中,还得继续挑选;若还剩物品(即n>1),问题即为从剩余的n-1件物品中选取若干个,使得他们的重量和等于s,即snap(s,n)—>snap(s,n-1). ③、若tn snap(s,n)—>snap(s,n-1);而选中的tn还要看snap(s-tn,n-1)是否有解,无解的话说明先取tn不合适,就要放弃tn,在剩余的物品中开始挑选,即有snap(s,n)—>snap(s,n-1). 【参考程序】 Constm=10; vart: array[1..m]ofinteger; x,y,i: integer;f: boolean; functionsng(x: integer): integer;{判断s-t[n]的三种情况} begin ifx>0thensng: =1 elseifx=0thensng: =0 elsesng: =-1; end; functionsnap(s,n: integer): boolean;{判断是否有解} begin casesng(s-t[n])of 0: beginwriteln(‘number: ’n: 4,‘weight: ’,t[n]: 4);snap: =true;end; 1: beginifn>1thenifsnap(s-t[n],n-1)=truethen beginwriteln(‘number: ’n: 4,‘weight: ’,t[n]: 4); snap: =true;end elsesnap: =snap(s,n-1) elsesnap: =false end; -1: ifn>1thensnap: =snap(s,n-1)elsesnap: =false end; end; begin writeln(‘thenumberofobject: ’);readln(y); writeln(‘totalweight=’);readln(x); fori: =1toydoread(t[i]);readln;{数据输入} f: =snap(x,y); {x: weight,y: number} ifnot(f)thenwriteln(‘notfound’); end. 例5、输出n个元素的无重复的全排列。 N个元素有n! 种不同排列。 分析: 1个元素直接输出; 2个元素有两种排列,如(ab)(ba); 3个元素以(abc)为例,有: abc acb bac bca cba cab 分析这些排列,一个简单的算法是: (1)a后随(bc)的所有排列 (2)b后随(ac)的所有排列 (3)c后随(ba)的所有排列 上面 (2)是将 (1)中的a、b互换位置;上面(3)是将 (1)中的a、c互换位置。 这里意味着可以用循环的方法来重复执行“交换位置,后随剩余序列的所有的排列”;而对剩余序列可以再使用这个方法,这就成了递归调用,后随的元素没有时,就到了递归的边界。 对于n个元素a=(a1a2…ak…an),设过程prem(a,k,n)是求a的第k到n个元素的全排列,设swap(a,k,I)是将a的第k个元素和第i个元素对换,i=k,…,n。 programexp1_7; vara: string;k,n: integer; procedureswap(vara: string;k,i: integer); vart: char; begin t: =a[k];a[k]: =a[i];a[i]: =t; end; procedureperm(a: string;k,n: integer); vari: integer; begin ifk=nthenwriteln(a) elsefori: =ktondo begin swap(a,k,i);perm(a,k+1,n); end end; begin readln(a);n: =length(a);perm(a,1,n); end. 例6、2的幂次方表示 任何一个正整数都可以用2的幂次方表示.例如: 137=2^7+2^3+2^0。 同时约定次方用括号来表示,即a^b可表示为a(b)。 由此可知,137可表示为: 2(7)+2(3)+2(0),进一步: 7=2^2+2+2^0 (2^1用2表示);3=2+2^0;所以最后137可表示为: 2(2 (2)+2+2(0))+2(2+2(0))+2(0)。 又如: 1315=2^10+2^8+2^5+2+1;所以1315最后可表示: 2(2(2+2(0))+2)+2(2(2+2(0)))+2(2 (2)+2(0))+2+2(0) 输入: 正整数(n≤20000) 输出: 符合约定的n的0,2表示(在表示中不能有空格) 【分析】: 递归法 2的幂次方由高幂向低幂分解 1. 计算最接近n(小于n)的2的次幂e 2e≤n<2e+1 设x=2e。 2.从e开始按照次幂递减的方向分解(0≤i≤e)设x1—项数。 若n≥x,则分解出x=2i的2幂次方表示: ⑴若x1>0(非第一项),则输出’+’; ⑵分析次幂i i=0,输出2(0); {递归边界} i=1,输出2; i>1,输出2(i的2幂次方表示) {递归} ⑶准备分解下一项 x1←x1+1;n←n-x; x←xdiv2;(无论n是否大于等于n) 由此得出算法: proceduresolve(n: word); var e,i,x1: byte; x: word; begin x←16384;e←14; {214<20000<215} whilex>ndo {计算最接近n(小于n)的x=2e} begin x←xdiv2;e←e-1 end;{while} x1←0; fori←edownto0do {逐位分解2的幂次i} begin ifn≥x thenbegin {若能分解x=2i} ifx1>0thenwrite(’+’); {若当前是中间项} caseiof {根据次幂i分解x的2幂次方表示} 0: write(’2(0)’); 1: write (2); elsebegin write(’2(’); solve(i); write(’)’) end{else} end;{case} x1←x1+1;n←n-x {准备分解下一项x=2i-1} end;{then} x←xdiv2 end{for} end;{solve} 例7、求数字的乘积根。 一个正整数的数字的乘积N的定义是: 这个整数中非零数字的乘积。 例如,整数999的数字乘积为9*9*9,即729。 729的数字乘积为7*2*9,即126。 126的数字乘积为1*2*6,即12。 12的数字乘积为1*2,即2。 一个正整数的数字乘积根N是这样得到的: 反复取该整数的数字乘积,直到得到一位数字为止。 例如,在上面的例子中数字的乘积根是2。 编写一个程序,输入一个正整数(长度不超过200位数字),输出计算其数字乘积根的每一步结果。 【分析】: 每一步得到的乘积根比它的上一步的位数要小,显然存在递归过程, 递归结束的条件是乘积根的位数等于1。 具体程序编码如下: programex5_17; varst: string; procedureinit; beginwriteln(‘pleaseinput: ’);readln(st);end; proceduremake(ss: string); vara,b: array[1..200]ofinteger; i,j,x,code: integer;w: string; begin fillchar(a,sizeof(a),0); fori: =1tolength(ss)do {取每一位数} val(ss[i],a[i],code); fillchar(b,sizeof(b),0); x: =1;b[1]: =1; fori: =1tolength(ss)do {每一位非0的数相乘} begin forj: =1toxdo ifa[i]<>0thenb[j]: =b[j]*a[i]; forj: =1toxdo {处理进位} begin b[j+1]: =b[j+1]+b[j]div10; b[j]: =b[j]mod10; end; ifb[x+1]>0thenx: =x+1; end; ss: =‘’; fori: =xdownto1do begin str(b[i],w);ss: =ss+w; end; writeln(ss); iflength(ss)>1thenmake(ss); end; begin init;{输入数据} writeln(st); make(st);{递归处理} readln; end. 例8、输入N个字符,然后以倒序输出(用递归实现)。 Programdigui(input,output); Constn=8; Typemat=array[1..n]ofchar; Vari: integer;a: mat; Procedureprint(i: integer); begin Ifi=nthenwrite(a[i]) elsebeginprint(i+1);write(a[i]);end; end; Begin fori: =1tondoreadln(a[i]); i: =1; print(i); readln; End. 例9、猴子选大王: 有n只猴子选大王,先从头到尾1至3报数,报到3的猴子退出,报至尾后,再从尾到头1至3报数,报到3的猴子退出…依次类推,当剩下两只猴子时,报1的为大王。 问若想当大王,应站在什么位置。 【分析】: 十只猴子1-10编号,则出圈的次序为 猴子编号: 1 2 3 4 5 6 7 8 9 10 出圈次序: 3 6 9 7 2 5 4 10 剩下8和1时,8号猴子报1为大王 程序如下: programhouzi(input,output); typearr=array[1..100]ofinteger; varh: arr;I,j,m,n: integer; procedurenum(vara: arr;varn,i: integer;m: integer); var f,k,s: integer; begin ifn>=3then begins: =0;k: =imod2;{k为1表示从头到尾,k为0表示从尾到头} casekof 1: forf: =1tomdo {正向报数} ifa[f]<>0then begin s: =s+1; ifs=3thenbegins: =0;n: =n-1;a[f]: =0;end; end; 0: forf: =mdownto1do{反向报数} ifa[f]<>0then begin s: =s+1; ifs=3thenbegins: =0;n: =n-1;a[f]: =0;end; end; end; i: =i+1;num(a,n,i,m);{递归调用}1),那么问题即可转换为从剩下的n-1件物品中选取若干件,使得他们的重量和等于包里剩下的可放入重量(s-tn),即:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- pascal 递归 算法 noip 竞赛 材料