页数:53页文件大小:1.65 MB来源: 计算机语言与程序设计ppt课件
本课件用三道例题深化递归:青蛙过河问题从无柱子情形归纳出递归基与递归公式并画出与或结点图;快速排序讲清分区处理、左右集合的递归排序与排序过程图示,给出sort函数与主程序;分书问题则用兴趣表、try(i)思路图与回溯三件事,完整演示回溯搜索的编程实现。
本页为第七讲 递归算法举例章节封面,本讲通过三个案例巩固递归编程思想:
本页为第七讲目录,列出本讲内容:青蛙过河、快速排序、分书问题、习题讨论。
本节为递归算法举例——青蛙过河,以 2000 年全国青少年信息学奥林匹克试题为例讲解递归求解思路。
讨论问题——青蛙过河。该题是 2000 年全国青少年信息学奥林匹克的一道试题,叙述如下:
一条小溪尺寸不大,青蛙可以从左岸跳到右岸。在左岸有一石柱 L,面积只容得下一只青蛙落脚,同样右岸也有一石柱 R,面积也只容得下一只青蛙落脚。有一队青蛙从尺寸上一个比一个小,将青蛙从小到大用 1,2,…,n 编号。规定初始时这队青蛙只能趴在左岸的石头 L 上,当然是一个落一个,小的落在大的上面,不允许大的在小的上面。
在小溪中有 S 个石柱、有 y 片荷叶,规定溪中的柱子上允许一只青蛙落脚,如有多只同样要求一个落一个,大的在下、小的在上;对于荷叶只允许一只青蛙落脚,不允许多只在其上;对于右岸的石柱 R,与左岸的石柱 L 一样允许多个青蛙落脚,但须一个落一个,小的在上、大的在下。
当青蛙从左岸的 L 上跳走后就不允许再跳回来;同样,从左岸 L 上跳至右岸 R,或从溪中荷叶或溪中石柱跳至右岸 R 上的青蛙也不允许再离开。问:在已知溪中有 S 根石柱和 y 片荷叶的情况下,最多能跳过多少只青蛙?
这题看起来较难,但是如果我们认真分析,理出思路,就可化难为易。
思路:
Jump(S,y)——最多可跳过河的青蛙数,其中 S 为河中柱子数、y 为荷叶数S=0,Jump(0,y)当 y=1 时 Jump(0,1)=2:河中有一片荷叶,可以过两只青蛙,起始时 L 上有两只青蛙,1# 在 2# 上面。
当 y=2 时 Jump(0,2)=3:河中有两片荷叶时,可以过 3 只青蛙。起始时 1#、2#、3# 三只青蛙落在 L 上:
采用归纳法:Jump(0,y)=y+1,意思是河中没有石柱的情况下,过河的青蛙数仅取决于荷叶数,数目是荷叶数+1。
再看 Jump(S, y),先看一个最简单情况:S=1,y=1。从图上看出需要 9 步,跳过 4 只青蛙:
| 时刻 | L1 | L2 | L3 | L4 | S1 | S2 | Y | R1 | R2 | R3 | R4 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| t=0 | 1 | 2 | 3 | 4 | — | — | — | — | — | — | — |
| t=1 | 2 | 3 | 4 | — | — | — | 1 | — | — | — | — |
| t=2 | 3 | 4 | — | — | — | 2 | 1 | — | — | — | — |
| t=3 | 3 | 4 | — | — | 1 | 2 | — | — | — | — | — |
| t=4 | 4 | — | — | — | 1 | 2 | 3 | — | — | — | — |
| t=5 | — | — | — | — | 1 | 2 | 3 | 4 | — | — | — |
| t=6 | — | — | — | — | 1 | 2 | — | 3 | 4 | — | — |
| t=7 | — | — | — | — | 2 | — | 1 | 3 | 4 | — | — |
| t=8 | — | — | — | — | — | — | 1 | 2 | 3 | 4 | — |
| t=9 | — | — | — | — | — | — | — | 1 | 2 | 3 | 4 |
说明:源文件该页以粘连字符与图形方式给出(L1~L4、S1~S2、Y、R1~R4 各位置按时刻 0~9 排列),上表依据前后页所述的九步跳法还原,供对照原图核对。
为了将过河过程描述得更清楚,给出了表 1。表中 L1 L2 L3 L4 表示左岸石柱上落在一起的青蛙的高度位置,L1 在最上面、L4 在最下面的位置,引入这个信息就可比较容易地看出对青蛙占位的约束条件;同理 R1 R2 R3 R4 也是如此。对水中石柱 S 也分成两个高度位置 S1、S2;对荷叶 Y 无须分层,因为它只允许一只青蛙落在其上。
这一过程是一分为二的过程:将 L 上的一队青蛙分解为两个队,每队各二只,且将上面的二只转移到了 S 上。这时可以考虑形成两个系统:一个是 L、Y、R 系统,一个是 S、Y、R 系统,前者二只青蛙号大、后者二只青蛙号小,先跳号大的、再跳号小的。从第五步到第九步可以看出的确是这么做的。
对于 LYR 系统,相当于 Jump(0,1);对于 SYR 系统,相当于 Jump(0,1)。两个系统之和为 2*Jump(0,1),因此有:
Jump(1,1) = 2*Jump(0,1) = 2*2 = 4现在再看 S=2、y=1 的 Jump(2,1):将河中的两个石柱称作 S1 和 S2,荷叶叫 y,考虑先将 L 上的青蛙的一半借助于 S2 和 y 转移到 S1 上,当然是一半小号的青蛙在 S1 上、大的留在 L 上。
这样 L S1 S2 y R 系统分解为:
(L S2 y R系统)+(S1 S2 y R系统) = 2 *(L S2 y R系统) = 2 * Jump(1,1)用归纳法得:Jump(S, y)=2*Jump(S-1, y)
将上述分析出来的规律写成递归形式的与或结点图如下。
举例:S=3,y=4,算 Jump(3,4)。由 Jump(0,y)=y+1 与 Jump(S,y)=2*Jump(S-1,y) 可得 Jump(3,4)=2*Jump(2,4)=4*Jump(1,4)=8*Jump(0,4)=8*5=40,即最多可跳过 40 只青蛙。
说明:源文件该页仅给出“举例:S=3,y=4,算 Jump(3,4)”的标题性内容,上式为依据前面递推关系补算的示例结果,供对照原图核对。
#include <stdio.h> int Jump(int, int);void main() { int s,y,sum; printf("请输入石柱数s=" ); scanf("%d",&s); printf("请输入荷叶数y=" ); scanf("%d",&y); sum=Jump(s,y); printf("Jump(%d,%d)=%d\n",s,y,sum); } int Jump(int r,int z){ int k; if (r==0) { k=z+1; } else { k=2*Jump(r-1,z); } return(k);}本节为递归算法举例——快速排序,讲解用递归思想实现快速排序的思路与程序。
快速排序的思路:
sort(l,r) 为将数组元素从下标为 l 到下标为 r 的 r-l+1 个元素从小到大排序我们画出与或图来阐述快速排序的思路。
分区处理:
A 结点表示将数组 a[l], a[l+1], …, a[r] 中的元素按由小到大用快速排序的思路排序;B 结点表示如果 l>=r,则什么也不做,这是直接可解结点;C 结点是在 l<r 情况下 A 结点的解。C 是一个与结点,要对 C 求解需分解为三步,依次为:
比如 a 数组中 a[l]=5,经分组处理后 5 送至 a[4]。5 到位后,其左边 a[0]~a[3] 的值都小于 5,其右边 a[5]、a[6] 大于 5。
本页以图形方式展示分区处理前后数组元素的变化:分区前数组 a 的下标 0~6 依次为 5、4、3、7、1、6、2,分区后为 4、3、1、2、5、7、6,5 落在下标 m=4 处,其左侧元素均不大于 5、右侧元素均大于 5。
说明:源文件该页为图示,数组数值与下标对应关系依据前后页程序与排序过程还原,建议对照原图核对。
下面按照这种思路构思一个快速排序的程序框图:
void sort(int array[ ], int ll, int rr) int l,r,i,k;本页以图形方式给出快速排序的程序框图,源文件未给出更多文字说明。
下面举例说明排序过程。
图1:a 数组中有 7 个元素待排序,让 k=a[l]=a[0]=5,下标 l=0、r=6。
2 进入直到型循环:
a[l]=a[r],即 a[0]=a[6]=4;l=l+1=0+1=1,见图2l=l+1=2,l 增 1 后的情况如图3。图3 的情况不再满足当循环条件a[r]=a[l],即 a[6]=a[2]=6,见图4。这时 l!=r 还得执行直到型循环的循环体r=r-1=6-1=5 见图5,之后退出当循环,因为 a[r]=3<ka[l]=a[r],并让 l=l+1=3,见图6l=l+1=4;a[4]=7>k,退出循环。见图7a[r]=a[l],a[5]=7。见图8,这时仍然 l<r,应继续执行直到型循环的循环体r=r-1=4。见图9a[l]=k,l=4、a[4]=5,这是 5 的最终位置,5 将整个数据分成左右两个集合,见图104、用上述思路去排左边的部分,从 l=0 到 r=3,见图11。让 k=a[l]=a[0]=4,然后进到直到型循环:
a[l]=a[r],l=l+1=1,见图12l=l+1=2;a[2]<k,l=l+1=3,这时 l=r,不会执行(4),同时退出直到型循环,见图13。然后做 a[l]=k,即 a[3]=4,见图14,左边也排好了4、用上述思路去排右边的部分,见图15,让 k=a[l]=a[5]=7,进入直到型循环:
a[l]=a[r]=6,l=l+1=5+1=6,见图16这时 l=r,不再执行(3)(4),退出直到型循环后,做 a[l]=k,见图17。
在有了递归调用函数之后,主程序很容易写,主程序中应包含:
下面给出参考程序(分两页)。
#include <stdio.h> void sort(int array[ ],int ll,int rr){ int l,r,i,k; if(ll<rr) { l=ll; r=rr; k=array[l]; do { while((l<r)&&(array[r]>=k)) r=r-1; if(l<r) { array[l]=array[r]; l=l+1; } while((l<r)&&(array[l]<=k)) l=l+1; if (l<r) array[r]=array[l]; } while(l!=r); array[l]=k; for(i=ll;i<=rr;i=i+1) { printf("a[%d]=%d;",i,array[i]); } printf("\n"); sort(array,ll,l-1); sort(array,l+1,rr); } }void main() { int a[10],i; printf("请输入10个整数\n" ); for (i=0;i<10;i=i+1) scanf("%d",&a[i]); sort(a,0,9); printf("排序结果为:"); for (i=0;i<10;i=i+1) printf("%d;",a[i]); printf("\n"); }本节为递归算法举例——分书问题。
题目:有编号分别为 1,2,3,4,5 的五本书,准备分给 A、B、C、D、E 五个人,每个人阅读兴趣用一个二维数组加以描述。
希望你写一个程序,输出所有分书方案,让人人皆大欢喜。
| 人\书 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| A | 0 | 0 | 1 | 1 | 0 |
| B | 1 | 1 | 0 | 0 | 1 |
| C | 0 | 1 | 1 | 0 | 1 |
| D | 0 | 0 | 0 | 1 | 0 |
| E | 0 | 1 | 0 | 0 | 1 |
解题思路:
int like[5][5]={ {0,0,1,1,0}, {1,1,0,0,1}, {0,1,1,0,1}, {0,0,0,1,0}, {0,1,0,0,1} };int book[5]={0,0,0,0,0};说明:源文件此处写作「book[i]=1」,与程序及题意不符,按标准写法应为 book[j]=1(以书号 j 为下标记录该书已被选用)。
try(i)——试着给第 i 人分书(i=0,1,…,4)本页以图形方式给出分书问题的与或图,源文件未给出更多文字说明。
说明:
take[i]=j 记住书 j 给了 i 人,同时记录 j 书已被选用,book[j]=1try(i+1)。如果 i==4,则应先使方案数 n=n+1,然后输出第 n 个方案下的每个人所得之书take[i]=-1 和 book[j]=0。这是在已输出第 n 个方案之后,去寻找下一个分书方案所必需的在有了上述的与或图之后,我们很容易写出一个程序框图。先看被调用函数 try(i) 的框图。
说明:源文件此处「同时记录 j 书已被选用」处写作 book[i]=1,与程序不符,按标准写法应为 book[j]=1。
本页以图形方式给出被调用函数 try(i) 的程序框图,源文件未给出更多文字说明。
#include <stdio.h> int take[5],n; int like[5][5] = { {0,0,1,1,0}, {1,1,0,0,1}, {0,1,1,0,1}, {0,0,0,1,0}, {0,1,0,0,1} }; int book[5]={0,0,0,0,0};下面讨论主程序应该做的事情:
try(0)参考程序如下所示(分三页):
void try(int i){ int j,k; for(j=0;j<=4;j=j+1) { if((like[i][j]>0)&&(book[j]==0)) { take[i]=j; book[j]=1; if(i==4) { n=n+1; printf("第%d个方案\n",n); for(k=0;k<=4;k=k+1) { printf("%d号书分给%c;\n",take[k],k+65); } printf("\n"); } else { try(i+1); } take[i]=-1; book[j]=0; } } }void main() { n=0; try(0);}本页为第七讲 递归算法举例的结束页,本讲内容至此结束。
下载地址:{{downurl}}
{{{a}}}
{{#b}}{{{b}}}
{{/b}} {{/title}} {{#nolog}}登陆注册
{{/nolog}} {{#djsx}}点我刷新
{{/djsx}} {{#gocz}} {{/gocz}} {{#ffqr}} {{/ffqr}}