收藏 分享(赏)

算法竞赛入门经典授课教案第2章 循环结构程序设计.doc

上传人:精品资料 文档编号:11058319 上传时间:2020-02-05 格式:DOC 页数:26 大小:368KB
下载 相关 举报
算法竞赛入门经典授课教案第2章 循环结构程序设计.doc_第1页
第1页 / 共26页
算法竞赛入门经典授课教案第2章 循环结构程序设计.doc_第2页
第2页 / 共26页
算法竞赛入门经典授课教案第2章 循环结构程序设计.doc_第3页
第3页 / 共26页
算法竞赛入门经典授课教案第2章 循环结构程序设计.doc_第4页
第4页 / 共26页
算法竞赛入门经典授课教案第2章 循环结构程序设计.doc_第5页
第5页 / 共26页
点击查看更多>>
资源描述

1、第 2 章 循环结构程序设计第 12 页 第 2 章 循环结构程序设计【教学内容相关章节】2.1for 循环 2.2 循环结构程序设计 2.3 文件操作2.4 小结与习题【教学目标】(1)掌握 for 循环的使用方法;(2)掌握 while 循环的使用方法;(3)学会使用计算器和累加器;(4)学会用输出中间结果的方法调试;(5)学会用计时函数测试程序效率;(6)学会用重定向的方式读写文件;(7)学会 fopen 的方式读写文件;(8)了解算法竞赛对文件读写方式和命名的严格性;(9)记住变量在赋值之前的值是不确定的;(10)学会使用条件编译指示构建本地运行环境。【教学要求】掌握 for 循环的使

2、用方法;掌握 while 循环的使用方法;掌握几个常用的文件操作库函数 fopen、fclose、fprintf、fscanf 等。【教学内容提要】在有些程序中,需要反复执行某些语句。将 n 条相同的语句简单地复制会使程序变得不合理的冗长,因此高级语言中提供了支持程序重复执行某一段程序的循环控制语句。相关的语句有:for、while、do while、break、continue 等。既可以从文件中读取数据, 也可以向文件中写入数据。读写文件之前,首先要打开文件。读写文件结束后,要关闭文件。C/C+提供了一系列库函数,声明于 stdio.h 中,用于进行文件操作。这里介绍其中几个常用的文件操作

3、库函数fopen、fclose、fprintf、fscanf 等。【教学重点、难点】教学重点:(1)掌握 for 循环的使用方法;(2)掌握 while 循环的使用方法;(3)掌握文件有关操作;(4)条件编译。教学难点:(1)掌握 for 循环的使用方法;(2)掌握 while 循环的使用方法;【课时安排(共 2 学时)】2.1for 循环 2.2 循环结构程序设计 2.3 文件操作2.4 小结与习题第 2 章 循环结构程序设计第 13 页 2.1 for 循环请用 for 循环实现输入正整数 n,打印 1,2,3,10,每个占用一行。程序如下:程序 2-1 输出 1,2,3,n 的值#inc

4、lude int main()int i, n;scanf(“%d“, for(i = 1; i #include int main()int a, b, n;double m;for(a = 1; a int main()int x, n, hi, lo;第 2 章 循环结构程序设计第 14 页 for(x = 1; ; x+) n = x * x;if(n 9999) break;hi = n / 100;lo = n % 100;if(hi/10 = hi%10 return 0;说明:本程序中用到 break 和 continue 语句。break 是指直接跳出本层循环,continu

5、e 是指结束本次循环,但不跳出本层循环,进入下一次循环。2.2 循环结构程序设计例 2-2 3n+1 问题。猜想:对于任意大于 1 的自然数,若 n 为奇数,则将 n 变为 3n+1,否则变为 n 的一半。经过若干次这样的变换,一定会使 n 变为 1。例如 3105168421。输入 n,输出变换的次数。n10 9。样例输入:3样例输出:7【分析】从 3n+1 问题可以看出,n 也不是“递增”式的循环,且循环次数也不确定,这种情况非常适合用 while 循环来实现。程序 2-4 3n+1 问题#include int main()int n, count = 0;scanf(“%d“, whi

6、le(n 1) if(n % 2 = 1) n = n*3+1;else n /= 2;count+;printf(“%dn“, count);return 0;提示 2-7:while 循环的格式为:“while(条件) 循环体;” 。从程序 2-4 中可得,while 循环与 for 循环可以相互转化。在本程序中的 count+,相当于一个计算器,这个功能在编程中会经常遇到。提示 2-8:当需要统计某种事物的个数时,可以用一个变量来充当计算器。提示 2-9:不要忘记测试。一个看上去正确的程序可能隐含错误。提示 2-10:在观察无法找出错误时,可以用“输出中间结果”的方法查错。例 2-3 阶

7、乘之和。输入 n,计算 S=1!+2!+3!+n!的末 6 位(不含前导 0) 。n10 6。这里,n!表示前 n个正整数之积。样例输入:10样例输出:37913【分析】第 2 章 循环结构程序设计第 15 页 用 S 变量来累加阶乘之和。核心算法只有一句话:for(i=1;iint main()int i, j, n, S = 0;scanf(“%d“, for(i = 1; i #include int main()const int MOD = 1000000;int i, j, n, S = 0;scanf(“%d“, for(i = 1; i int main()int x, n =

8、 0, min, max, s = 0;while(scanf(“%d“, if(x max) max = x;n+;printf(“%d %d %.3lfn“, min, max, (double)s/n);return 0;说明:(1)scanf 函数的返回值是成功输入的变量个数。当输入结束时,scanf 无法再次读取 x,将返回 0。(2)在测试时,输入 2 8 3 5 1 7 3 6,按 Enter 键后,没有输出结果,所以此时按Enter 键并不意味着输入的结束。提示 2-15:在 Windows 下,输入完毕后先按 Enter 键,再按 Ctrl+Z 键,最后再按Enter 键,即

9、可结束键入。在 Linux 下,输入完毕后按 Ctrl+D 键即可结束输入。通过提示 2-15,输入可以结束了。但是此程序的运行结果是不确定的。提示 2-16:变量在未赋值之前的值是不确定的。特别地,它不一定等于 0。解决上面程序的运行结果不对的方法是在变量使用前赋初值。由于 min 保存的是最小值,它的初值应该是一个很大的数;反过来,max 的初值应该是一个很小的数。一种方法是定义一个很大的常数,如 INF=1000000000,然后让 max=INF,而 min=-INF,而另一种方法是先读取第一个整数 x,然后令 max=min=x。另一个好的方法是用文件把输入数据保存在文件中,输出数据

10、也保存在文件中。事实上,几乎所有算法竞赛的输入数据和标准答案都是保存在文件中的。使用文件最简单的方法是使用输入输出重定向,只需要在 main 函数的入口处加入以下两条语句:freopen(“input.txt“,“r“,stdin);freopen(“output.txt“,“w“,stdout);它将使得 scanf 从文件 input.txt 读入,printf 写入文件 output.txt。但是并不是所有算法竞赛都允许用程序读写文件。甚至有的竞赛允许访问文件,但不允许 freopen 这样的重定向方式读写文件。请参赛之前仔细阅读文件读写的相关规定。第 2 章 循环结构程序设计第 17

11、页 提示 2-17:请在比赛之前了解文件读写的相关规定:是标准输入输出(也称标准I/O,即直接读键盘、写屏幕) ,还是文件输入输出?如果是文件输入输出,是否禁止用重定向方式访问文件?多年来,无数选手因文件相关问题丢掉了大量的得分。一个普适的原则是:详细阅读比赛规定,并严格遵守。例如,如果题目规定程序名称为 test,输入文件名为 test.in,输出文件名为test.out,就是不要犯以下错误。错误 1:程序存为 t1.c(应该改成 test.c) 。 错误 2:从 input.txt 读取数据(应该从 test.in 读取) 。 错误 3:从 tset.in 读到数据(拼写错误,应该从 te

12、st.in 读取) 。 错误 4:数据写到 test.ans(扩展名错误,应该是 test.out) 。 错误 5:数据写到 c:contesttest.out(不能加路径,哪怕是相对路径。文件名应该只有 8 个字符:test.out) 。提示 2-18:在算法竞赛中,选手应严格遵守比赛的文件名规定,包括程序文件名和输入输出文件名。不要弄错大小写,不要拼错文件名,不要使用绝对路径或相以路径。 利用文件是一种好的自我测试方法,但如果比赛要求采用标准输入输出,就必须在自我测试完毕之后删除重定向语句。下面来介绍一种方法可以在本机测试时用文件重定向,但一旦提交到比赛,就自动“删除”重定向语句。代码如下

13、:程序 2-8 数据统计(重定向版)#define LOCAL#include #define INF 1000000000int main()#ifdef LOCALfreopen(“data.in“, “r“, stdin);freopen(“data.out“, “w“, stdout);#endifint x, n = 0, min = INF, max = -INF, s = 0;while(scanf(“%d“, if(x max) max = x;/*printf(“x = %d, min = %d, max = %dn“, x, min, max);*/n+;printf(“%

14、d %d %.3lfn“, min, max, (double)s/n);return 0;上面是一份典型的比赛代码,包含了几个特别的地方:(1)重定向的部分被写在了#ifdef 和#endif 中。它的含义是:只有定义了符号LOCAL,才编译两条 freopen 语句。(2)输出中间结果的 printf 语句写在注释中它在最后版本的程序中不应该出现,但是又舍不得删除它(万一发现了新的 bug,需要再次用它输出中间信息) 。把它注释化的好处是:一旦需要的时候,把注释符去掉即可。上面的代码在程序首部就定义了符号 LOCAL,因此在本机测试时使用重定向方式读写文件。如果比赛要求读写标准输入输出,只

15、需在提交之前删除#define LOCAL 即可。一个更重好的方法是在编译选项而不是程序里定义这个 LOCAL 符号,这样在提交之前不需要修改程序。第 2 章 循环结构程序设计第 18 页 如果比赛要求用文件输入输出,但禁止用重定向的方式,程序如下:程序 2-9 数据统计(fopen 版)#include #define INF 1000000000int main()FILE *fin, *fout;fin = fopen(“data.in“, “rb“);fout = fopen(“data.out“, “wb“);int x, n = 0, min = INF, max = -INF,

16、s = 0;while(fscanf(fin, “%d“, if(x max) max = x;n+;fprintf(fout, “%d %d %.3lfn“, min, max, (double)s/n);fclose(fin);fclose(fout);return 0;说明:(1)本程序先声明变量 fin 和 fout,把 scanf 改成 fscanf,第一个参数为fin;把 printf 改成 fprintf,第一个参数为 fout,最后执行 fclose,关闭两个文件。(2)重定向和 fopen 的区别。重定向的方法写起来简单、自然,但是不能同时读写文件和标准输入输出;fopen

17、的写法稍显繁琐,但是灵活性比较大。如果想把 fopen 版的程序改成读写标准输入输出,只需赋值 fin=stdin;fout=stdout;即可,不要想调用 fopen和 fclose。2.4 小结与习题2.4.1 输出技巧首先是输出技巧。通过对程序 2-1 进行小小的改动来实现输出 2,4,6,8,2n,每个一行。为了方便,现把程序复复制如下:1 #include 2 int main()3 4 int i, n;5 scanf(“%d“, 6 for(i = 1; i int main()double i;for(i=0;i!=10;i+=0.1)printf(“%.1lfn“,i);re

18、turn 0;说明:对于 i 可以达到 10.0,但永远不会与 10 相等,所以 for 循环是一个死循环。对于 float 和 dobule 类型的数据不能直接用 =和!=来比较大小,即不要测试精确的浮点型数值,需要用精度比较,来测试一个可接受的数值范围。课本 P85 页有这样的说明。例如,if(fabs(sales_tax-0.065)#include int main()_int64 n, /* 注意 int64 之前是两个下划线(_)*/x,count=0; /* 假设 x 是 n 的因子,count 为计数器 */scanf(“%I64d“,for(x=1;x /功能和 C 中的 s

19、tdio.h 很接近,但有些许不同第 2 章 循环结构程序设计第 20 页 using namespace std;int main()int a,b;while(scanf(“%d%d“, return 0;上面的 C+程序很像 C 程序。唯一的区别是原来的 stdio.h 变成了 cstdio,并且多了一条语句 using namespace std。这是一个普遍规律要在 C+程序中使用 C 语言头文件,请去掉扩展名.h,并在最前面加上小字母 c。例如,stdio.h 在 C+中的新名字是 cstdio。另外,第一行中以/开头的是 C+特有的“单行注释”,它和 C 中的传统注释(/*和*/

20、)可以混合使用。(2)方法二(不用记住%d、%lf 等恼人的占位符)#include /头文件 iostream 包含了对输入输出流的定义using namespace std;int main()int a,b;while(cinab) coutusing namespace std;ifstream fin(“aplusb.in“);ofstream fout(“aplusb.out“);int main()int a, b;while(finab) fout)从流中输入数据。比如本程序中有一个输入流(fin),finab 表示从输入流中读取两个指定类型(即变量 a 和 b 的类型)的数据

21、。插入器(void main()int a,b;scanf(“%d %d”,printf(“%d”,a+b);但上述代码提交上述到 OJ 上不能通过,是什么原因呢?这就是下面需要解决的问题。下面来分类进行介绍。2基本输入(1)第一类输入输入不说明有多少个 Input Block,以 EOF 为结束标志,例如上面的例子。源代码如下:#include int main()第 2 章 循环结构程序设计第 22 页 int a,b;while(scanf(“%d %d“, 本类问题的输入方案为:C 语法while(scanf(“%d %d“, ,如果只有一个整数输入,返回值是 1;如果有两个整数输入,

22、返回值是 2;如果一个都没有,则返回值是-1。EOF 是一个预定义的常量,等于-1。(2)第二类输入输入一开始就会说有 n 个 Input Block,下面接着是输入 n 个 Input Block。参见:HDOJ_1090( http:/ int main() int n,i,a,b;scanf(“%d“, for(i=0;i n; for( i=0 ; iint main() int a,b;while(scanf(“%d %d“, 上面程序有什么问题?本类问题的输入方案为:C 语法while(scanf(“%d”, gets(buf); C+语法如果用 string buf;来保存,则用

23、语句 getline( cin , buf ); 。如果用 char buf 255 ; 来保存,则用语句 cin.getline( buf, 255 );。说明:scanf(“%s%s“,str1,str2),在多个字符串之间用一个或多个空格分隔;若使用 gets 函数,应为 gets(str1); gets(str2); 字符串之间用回车符作分隔。通常情况下,接受短字符用 scanf 函数,接受长字符用 gets 函数。getchar 函数每次只接受一个字符,经常 c=getchar()这样来使用。cin.getline 的用法getline 是一个函数,它可以接受用户的输入的字符,直到已

24、达指定个数,或者用户输入了特定的字符。它的函数声明形式(函数原型)如下:istream不用管它的返回类型,来关心它的三个参数:char line就是一个字符数组,用户输入的内容将存入在该数组内。int size 表示最多接受几个字符?用户超过 size 的输入都将不被接受。char endchar 表示当用户输入 endchar 指定的字符时,自动结束。默认是回车符。结合后两个参数,getline 可以方便地实现:用户最多输入指定个数的字符,如果超过,则仅指定个数的前面字符有效,如果没有超过,则用户可以通过回车来结束输入。char name4;第 2 章 循环结构程序设计第 24 页 cin.

25、getline(name,4,n);由于 endchar 默认已经是 n,所以后面那行也可以写成:cin.getline(name,4);。思考:以下题目属于哪一类输入?http:/ Input Block 对应一个 Output Block,Output Block 之间没有空行。参见:HDOJ_1089( http:/ 语法printf(“%dn“,ans); C+语法 .cout int main() int a,b;while(scanf(“%d %d“, 解决办法如下:C 语法printf(“%dnn“,ans); C+语法. cout int main() int icase,n,

26、i,j,a,sum;scanf(“%d“,第 2 章 循环结构程序设计第 25 页 for(i=0;i#include int main()int j=0;for(j=0;j总结-练习-总结-的过程,使得自己编程水平得到提高;(2)可以在 http:/ 或其它网站上多多练习;(3)可以去杭电 ACM 论坛或其它 ACM 论坛上,与别人分享经验;(4)有问题可以在 google、baidu 上去搜索。2.4.6 小结现在对本章介绍的循环总结如下:(1)介绍了在编程中的一些经验,如使用计算器、累加器、以及“当前最小/最大值”这样的中间变量。使用 printf 输出一些关键的中间变量能有效地帮助我们

27、了解程序执行过程、发现错误。(2)遇到问题需要自己分析和设计,可以先写出“伪代码”,然后转换成程序。伪代码是为了让思路更清晰,突出主要矛盾。(3)编写好程序后,测试显得相当重要。如前面的例子中几乎都有陷阱:运算结果溢出、运算时间过长等。对于海量的输入输出问题可以通过文件得到缓解。(4)再次强调:编程不是看书看会的,也不是听课听会的,而是练会的。第 2 章 循环结构程序设计第 27 页 2.4.7 上机练习注意,本章的题目需用文件输入输出。如果题目代号为 abc,那么输入文件为abc.in,输出文件为 abc.out。如果对文件操作不熟练,请尽量把 fopen、freopen 两种方法都尝试一下

28、。习题 2-1 位数(digit)输入一个不超过 109的正整数,输出它的位数。例如 12735 的位数是 5。请不要使用任何数学函数,只用四则运算和循环语句实现。【分析】采取的策略是不断地整除 10,检查商是否大于 10。程序如下:#include void main() unsigned long n; /* 无符号的长整型数,占 4 字节存储空间 */int digit=1; /* 位数 */scanf(“%ld“,for (;n=10;) n=n/10;digit=digit+1;printf(“%d“,digit);return;习题 2-2 水仙花数(daffodil)输出 100

29、999 中的所有水仙花数。若 3 位数 ABC=A2+B2+C2,则称其为水仙花数。例如153=13+53+33,所以 153 是水仙花数。【分析】采取的策略是穷举 100999 之内的所有数据,检查是否满足题目的要求。程序如下:#include void main() int n,a,b,c; /* n 的百、十、个位 */for (n=100; nvoid main () int a, /*三人一排,余 a 人*/b, /*五人一排,余 b 人*/c, /*七人一排,余 c 人*/sum; /*总人数*/scanf(“%d%d%d“,for (sum=10; sum100) printf(

30、“No answer“);return;习题 2-4 倒三角形(triangle)输入正整数 n20,输出一个 n 层的倒三角形。例如 n=5 时输出如下:#【分析】该图形一共有 n 行,这次要考虑每行中,先输出若干个空格,所以,其外循环为:for( i=1;ivoid main () int n, /* 输出 n 行; nmain()int i,j;for(i=1;iint main() int i,j,k;for(i=1;iint main() int i,j;char ch;for(i=1;imain() int i,j,k;for(i=1;ivoid main() int m,n,i,

31、newData, /*新读到的数据*/count; /*计数器*/freopen(“stat.in.txt“,“r“,stdin);freopen(“stat.out.txt“,“w“,stdout);scanf(“%d“,for (i=1; ivoid main () 第 2 章 循环结构程序设计第 34 页 int n,i,m,newData, /*新输入的数据*/count; /*计数器*/scanf(“%d“,scanf(“%d“,count=0;for (i=1; ivoid main () int n,i,m,newData, /*新输入的数据*/count; /*计数器*/fre

32、open(“统计的变种.in.txt“,“r“,stdin);freopen(“统计的变种.out.txt“,“w“,stdout);scanf(“%d“,scanf(“%d“,count=0;for (i=1; i第 2 章 循环结构程序设计第 35 页 void main () unsigned int n; /*我们认为 int 已经足够表达所谓的“正整数”范围*/double sum=0;scanf(“%u“,for (; n=1; n=n-1) sum=sum+1.0/n;printf(“%.3f“,sum);return;习题 2-7 近似计算(approximation)计算 ,

33、直到最后一项小于 10-6。14357【分析】本题主要考察循环语句的使用。秘诀是要从绝对值最小的项开始向绝对值大的项累加。程序如下:#include void main () int n;double sum=0;int symbol;n=1000001; /*显然,1/1000001 小于 0.000001*/for (; n=1;n=n-2)if (n+1)/2%2=1) symbol=1;else symbol=-1;sum=sum+symbol*(1.0/n);printf(“%.20lf“,sum);return;习题 2-8 子序列的和(subsequence)输入两个正整数 nv

34、oid main () double m,n;double sum=0;scanf(“%lf%lf“, /* 假设用户输入的 n 总是不大于 m */for (; m=n; m=m-1) sum=sum+1.0/(m*m); /*注意,不能写成 sum=1/m*m */printf(“%.5lf“,sum);return;第 2 章 循环结构程序设计第 36 页 习题 2-9 分数化小数(decimal)输入正整数 a,b,c,输出 a/b 的小数形式,精确到小数点后 c 位。a,b10 6,c100。例如 a=1,b=6,c=4 时应输出 0.1667。【分析】注意到 a,b,c 均为正整数

35、,所以输出结果的小数点后至少会有 1 位。另外,从本题的示例输出中可以感知到,小数的最后一位,是要考虑到四舍五入进位的。采取的策略是模拟整数除法的过程,利用整除所得的余数,不断地对目标结果求精。程序如下:#include void main() unsigned int a,b,c,x, y; /* 小数点后的位数 */scanf(“%d%d%d“,x=a/b;printf(“%d.“,x);x=a%b;y=1;for (; y=5)printf(“%d“,x/b+1);elseprintf(“%d“,x/b);x=x%b;return;习题 2-10 排列(permutation)用 1,2

36、,3,9 组成 3 个三位数 abc,def 和 ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3。输出所有解。提示:不必太动脑筋。【分析】采用的策略是穷举法解题。尽管说“不必太动脑筋” ,但也不意味着“一点儿都不用动脑筋” 。如果说三个三位数是 abc, def, ghi,比率是 1:2:3,那么显然 d=(2*a)并且g=3*a。所以,1=2a,g=3a;这些论断,都可以减少电脑穷举所需扫描的范围,提高运行速度。如果还能够提出更加精确的论断,就还能进一步提高程序的运行速度。程序如下:#include void main () int a,b,c,d,e,f,g,h,i;

37、for (a=1; a=3; a=a+1)for (b=1; b=9; b=b+1) /*1*/第 2 章 循环结构程序设计第 37 页 if (b!=a)for (c=1; c=9; c=c+1) /*2*/if (c!=a d=9; d=d+1) /*3*/if (d!=a e=9; e=e+1) /*4*/if (e!=a f=9; f=f+1) /*5*/if (f!=a g=9; g=g+1) /*6*/if (g!=a h=9; h=h+1) /*7*/if (h!=a i=9; i=i+1) /*8*/if (i!=a /*8*/ /*7*/ /*6*/ /*5*/ /*4*/ /*3*/ /*2*/ /*1*/return;布 置 作 业上机练习 习题 2-4、2-7、2-9、2-10

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报