算法竞赛入门经典(第2版)学习笔记2

  • 第二章 循环结构程序设计
    • 2.1 for循环
    • 2.2 while 循环和do-while 循环
    • 2.3 循环的代价
    • 2.4 算法竞赛中的输入输出框架
    • 2.5 注解与习题

第二章 循环结构程序设计

2.1 for循环

例题2-1 aabb
输出所有形如aabb的4位完全平方数(即前两位数字相等,后两位数字也相等)

分析:
第一种思路
(1)首先枚举所有可能的aabb,然后判断他们是否为完全平方数
注:a的范围是1~9,但b可以等于0所以范围为0 ~ 9
可以写出下列伪代码:

for(int a=1;a<=9;a++)for(int b=0;b<=9;b++)if(aabb是完全平方数)printf("%d\n",aabb);

注1:不拘一格地使用伪代码来思考和描述算法是一种值得推荐的做法
(2)写出伪代码之后,我们需要考虑如何将它变成真正的代码,上面的伪代码有两个“非法”之处,一个是完全平方数的判定,另一个是aabb这个变量,后者用变量n=a1100+b11存储即可
(3)接下来,如何判断n是否完全平方数?可以将其开平方后,看它是否为整数,即用一个int型变量m存储sqrt(n)四舍五入后的整数,然后判断m2是否等于n。函数floor(x)在c语言的库函数中,是返回不超过x的最大整数

程序2-2 7744问题第一种解法

#include<stdio.h>
#include<math.h>
int main(){int n;for(int a=1;a<=9;a++)for(int b=0;b<=9;b++){n=a*1100+b*11;int m=floor(sqrt(n)+0.5);//注意此处的0.5,为了四舍五入减小误差if(m*m==n) printf("%d",n);}
}

为了减少误差的影响,一般改成四舍五入,即floor(x+0.5)
注2:浮点运算可能存在误差,在进行浮点数比较时,应考虑到浮点误差
第二种思路
枚举平方根x,从而可以避免开平方的操作

程序2-3 7744问题第二种解法

#include<stdio.h>
int main(){for(int i=1; ;i++){int m=i*i;if(m<1000) continue;if(m>9999) break;int high=m/100;//high是高两位int low=m%100;//low是低两位if(high%10 == high/10 && low%10 == low/10)printf("%d",m);}
}

(1)continue是指跳回for循环的开始,执行调整语句并判断循环条件(即“直接进行下一次循环”),而break是指直接跳出循环。
(2)这里continue语句的作用是排除不足四位数的m,直接继续检查后面的数。continue可以帮助我们偷懒,不必求出循环的起始点。有了break,连循环的终点也可以不用指定,当n超过9999时会自动退出循环。
(3)注意这里的for循环语句是残缺的,没有制定循环条件。事实上,3部分都是可以省略的,for(;;)是一个死循环,如果不采取措施(如break),就永远不会结束

2.2 while 循环和do-while 循环

注1:C99并没有规定int类型的确切大小,但在当前流行的竞赛平台中,int都是32位整数,范围是-2147483648~2147483648。

注2:当普通的int类型溢出时,可以使用long long解决问题,范围是-263~263-1,唯一的区别是要把输入时的%d改成%lld(Linux下)。有时Windows平台还要把%lld改成%I(i)64d,很容易搞错,所以如果涉及long long的输入输出,常用C++的输入输出流或自定义的输入输出方法。

2.3 循环的代价

例题2-4 阶乘之和
输入n,计算S=1!+2!+3!+···+n!的末6位(不含前导0)。n<=106
样例输入:
10
样例输出:
37913

#include<stdio.h>
int main(){int n,sum,s;sum=0;s=1;scanf("%d",&n);for(int i=1;i<=n;i++){s*=i;sum+=s;}printf("%d",sum%1000000);
}

但如果测试n=100时,则输出-971703,代表乘法溢出了
它的速度太慢!于是把程序改成“每步取模”的形式,然后加一个“计时器”
代码如下:

#include<stdio.h>
#include<time.h>
int main(){const int MOD=1000000;int n,sum,s;sum=0;s=1;scanf("%d",&n);for(int i=1;i<=n;i++){s=s*i%MOD;sum=(sum+s)%MOD;}printf("%d",sum%1000000);printf("Time used=%.2f\n",(double)clock() / CLOCKS_PER_SEC);
}

输入“20”,系统瞬间输出了答案820313。
注1:可以使用time.h和clock()函数获得程序运行时间。常数CLOCKS_PER_SEC和操作系统相关,请不要直接使用clock()的返回值,而应总是除以CLOCKS_PER_SEC。
注2:要计算只包含加法、减法和乘法的整数表达式除以正整数n的余数,可以在每步计算之后对n取余,结果不变。
注3:很多程序的运行时间与规模n存在着近似的简单关系。可以通过计时函数来发现或验证这一关系
注4:本节展示了循环结构程序设计中最常见的两个问题:算术运算溢出和程序效率低下。另外,本节中介绍的两个工具——输出中间结果和计时函数,都是相当实用的。

2.4 算法竞赛中的输入输出框架

例题2-5 数据统计
输入一些整数,求出他们的最小值、最大值和平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。
样例输入:
2 8 3 5 1 7 3 6
样例输出:
1 8 4.375

分析:此题的关键在于:整数的个数是不确定的,下面首先给出第一个程序:

程序2-9 数据统计(有bug)

#include<stdio.h>
int main(){int x,n=0,min,max,s=0;while(scanf("%d",&x)==1){s+=x;if(x<min) min=x;if(x>max) max=x;n++;}printf("%d %d %.3f\n",min,max,(double)s/n);
}

(1)scanf函数有返回值,返回的是成功输入的变量个数,当输入结束时,scanf函数无法再次读取x,将返回0。
(2)测试程序时,输入“2 8 3 5 1 7 3 6”,按回车键,但并未显示任何结果。这是因为scanf的输入格式,空格、TAB、回车符都是无关紧要的,所以按回车键并不意味着输入的结束。
注1:在windows下,输入完毕后先按回车键,再按Ctrl+Z键,最后再按回车键即可结束输入。在Linux下,输入完毕后按Ctrl+D键即可结束输入
注2:变量在未赋值之前的值是不确定的。特别地,它不一定等于0。所以要在使用之前赋值。
(3)一种方法是定义一个很大的常数,如INF=1000000000,然后让max=-INF,而min=INF,另一种方法是先读取第一个整数x,然后令max=min=x。

综上所述,以上的程序存在一定的不便,所以一个好的方法是用文件——把输入数据保存在文件中,输出数据也保存在文件中。
事实上,几乎所有算法竞赛的输入数据和标准答案都是保存在文件中的。
使用文件最简单的方法是使用输入输出重定向,只需在main函数的入口处加入以下两条语句:
freopen(" input.txt “,” r ", stdin );
freopen(" output,txt “,” w ", stdout );
上述语句将使得scanf从文件input,txt读入,printf写入文件output.txt。
尽管这样做很方便,并不是所有算法竞赛都允许用程序读写文件。甚至有的竞赛允许访问文件,但不允许用freopen这样的重定向方式读写文件。参赛之前请仔细阅读文件读写的相关规定。

注3:请在比赛之前了解文件读写的相关规定:是标准输入输出(即直接读键盘、写屏幕),还是文件输入输出?如果是文件输入输出,是否禁止用重定向方式访问文件?
注4:输入输出文件名和程序名往往都有着严格规定,不要弄错大小写,不要拼错文件名,不要使用绝对路径或相对路径。

利用文件是一种很好的自我测试方法,但如果比赛要求采用标准输入输出,就必须在自我测试完毕之后删除重定向语句。
有一种方法可以在本机测试时用文件重定向,但一旦提交到比赛,就自动“删除”重定向语句。
代码如下:

程序2-10 数据统计(重定向版)

#define LOCAL
#include<stdio.h>
#define INF 1000000000
int 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",&x)==1){s+=x;if(x<min) min=x;if(x>max) max=x;/*printf("x=%d,min=%d,max=%d\n",x,min,max);*/n++;}printf("%d %d %.3f\n",min,max,(double)s/n);
}

重定向的部分被写在了#ifdef和#endif中,其含义是:只有定义了符号LOCAL才编译两条freopen语句。
所以最后只需在提交之前删除#define LOCAL即可。

如果比赛要求用文件输入输出,但禁止用重定向的方式,又当如何,程序如下:

程序2-11 数据统计(fopen版)

#include<stdio.h>
#define INF 1000000000
int main(){FILE *fin,*fout;fin=fopen("data.in","rb");fout=fopen("data.out","wb");int x,n=0,min=INF,max=-INF,s=0;while(fscanf(fin,"%d",&x)==1){s+=x;if(x<min) min=x;if(x>max) max=x;n++;}fprintf(fout,"%d %d %.3f\n",min,max,(double)s/n);fclose(fin);fclose(fout);
}

把scanf改成fscanf,第一个参数为fin;把printf改成fprintf,第一个参数为fout,最后执行fclose,关闭两个文件
注5:在算法竞赛中,如果不允许使用重定向方式读写数据,应使用fopen和fscanf/fprintf进行输入输出。
如果想把fopen版的程序改成读写标准输入输出,只需赋值“fin=stdin;fout=stdout;"即可,不要调用fopen和fclose。

例题2-6 数据统计Ⅱ
输入一些整数,求出它们的最小值、最大值和平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。
输入包含多组数据,每组数据第一行是整数个数n,第二行是n个整数。n=0为输入结束标记,程序应当忽略这组数据。相邻两组数据之间应输出一个空行。
样例输入:
8
2 8 3 5 1 7 3 6
4
-4 6 10 0
0
样例输出
Case 1:1 8 4.375
(这里是空行)
Case 2:-4 10 3.000

分析:本题和例题2-5本质相同,但是输入输出方式有一定的变化。由于这样的格式在算法竞赛中非常常见。所以在这里展开。

程序2-12 程序统计Ⅱ

#include<stdio.h>
#define INF 1000000000
int main(){int x,n=0,min=INF,max=-INF,s=0,kase=0;while(scanf("%d",&n)==1 && n){int s=0,min=INF,max=-INF;for(int i=0;i<n;i++){scanf("%d",&x);s+=x;if(x>max) max=x;if(x<min) min=x;}if(kase) printf("\n");printf("Case %d: %d %d %.3f\n",++kase,min,max,(double)s/n);} return 0;
}
  • 输入循环问题。题目说了n=0位输入标记,为什么还要判断scanf的返回值?答案是:为了鲁棒性(robustness)。有时会出现题目指明以n=0为结束标记而真实数据忘记以n=0结尾的情形。
    注6:在算法竞赛中,偶尔会出现输入输出错误的情况。如果程序鲁棒性强,有时能在数据有瑕疵的情况下仍然给出正确的结果。程序的鲁棒性在工程中也非常重要。
  • kase是当前数据编号的计数器

注7:在多数据的题目中,一个常见的错误是:在计算完一组数据后某些变量没有重置,影响到下组数据的求解。
注8:当嵌套的两个代码块中有同名变量时,内层的变量会屏蔽外层变量,有时会引起十分隐蔽的错误。

2.5 注解与习题

习题2-1 水仙花数
输出100~999中的所有水仙花数。若3位数ABC满足ABC=A3+B3+C3,则称其为水仙花数。例如153=13+53+33,所以153是水仙花数

#include<stdio.h>
int main(){int a,b,c,x,y,z;for(a=1;a<=9;a++)for(b=0;b<=9;b++)for(c=0;c<=9;c++){int m=a*100+b*10+c;x=a*a*a;y=b*b*b;z=c*c*c;if(m == (x+y+z))printf("%d%d%d\n",a,b,c); }
}

另一种方法可以从分别求出个位、十位、百位进行考虑

习题2-2 韩信点兵
相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。输入包含多组数据,每组数据包含3个非负整数a,b,c,表示每种队形排尾的人数(a<3,b<5,c<7),输出总人数的最小值(或报告无解)。已知总人数不小于10,不超过100。输入到文件结束为止。
样例输入:
2 1 6
2 1 3
样例输出:
Case 1:41
Case 2:No answer

#include<stdio.h>
int main(){int a,b,c,count=0;while(scanf("%d %d %d",&a,&b,&c)==3){for(int i=10;i<=100;i++){if(i%3==a && i%5==b && i%7==c){printf("Case %d: %d\n",++count,i);break;}else if(i==100)printf("Case %d: No answer\n",++count);}}
}

习题2-3 倒三角形
输入正整数n<=20,输出一个n层的倒三角形。

#include<stdio.h>
int main(){int n;scanf("%d",&n);for(int i=1;i<=n;i++){for(int j=1;j<i;j++)printf(" ");for(int z=1;z<=(n-i)*2+1;z++)printf("#");printf("\n"); }
}

思路:注意要找到每行输出’#'的个数和行数的规律,以及每行空格数的规律。

习题2-4 子序列的和
输入两个正整数n<m<106,输出1/n2+1/(n+1)2+···+1/m2,保留5位小数。输入包含多组数据,结束标记为n=m=0。

#include<stdio.h>
int main(){int n,m,count=0;while(1){ //指的是无限循环,直到遇到控制条件(如break) double sum=0;scanf("%d%d",&n,&m);if(n==m && n==0) break;for(int i=n;i<=m;i++){//sum+=1.0/(i*i);//这样会溢出//sum+=1.0/i/i;//另一种不会溢出的写法sum+=(1.0/i)*(1.0/i);}printf("Case %d:%.5f\n",++count,sum);}
}

注:

  • 在输入包含多组数据的题型中,要记得每次循环开始时,为变量重置,否则会导致后面计算出错。
  • 掌握while(1)的用法。
  • 注意乘法溢出问题!

习题2-5 分数化小数
输入正整数a,b,c,输出a/b的小数形式,精确到小数后c位。a,b<=106,c<=100。输入包含多组数据,结束标记为a=b=c=0。
样例输入:
1 6 4
0 0 0
样例输出:
Case 1:0.1667

#include<stdio.h>
int main(){int a,b,c,count=0;while(1){scanf("%d%d%d",&a,&b,&c);double a1=a;double b1=b;if(a==b && b==c && c==0)break;printf("Case %d:%.*lf\n",++count,c,a1/b1);}
}

注:

  • count为计数器
  • a和b一开始输入时要求是整数,但后面相除为小数形式,所以后面需要改变成double类型,也可以在别的地方进行改变,比如在最后用1.0*a/b或者在最后(double)a/(double)b
  • %.*lf是特殊的输出符,可以用变量表示小数点后的尾数,对我而言是一个新的知识,要记住!

习题2-6 排列
用1,2,3,···,9组成3个三位数abc,def和ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3,按照“abc def ghi”的格式输出所有解,每行一个解。提示:不必太动脑筋。

#include<stdio.h>
int main(){for(int i=123;i<=329;i++){int n=i*2;int m=i*3;if((i%10+i/10%10+i/100+n%10+n/10%10+n/100+m%10+m/10%10+m/100)==45&& ((i%10)*(i/10%10)*(i/100)*(n%10)*(n/10%10)*(n/100)*(m%10)*(m/10%10)*(m/100))==362880)printf("%d %d %d\n",i,n,m);}
}

注:

  • 第一个关键问题在于1:2:3可以说明假设用一个最小的三位数就可以表示出其他的二个数,然后就是最小的三位数是123,它的最大值是987/3得到329,所以1倍的三位数abc的范围就是123~329。

  • 第二个关键问题在于:如何每个数字恰好使用一次,而解决办法是1 ~ 9 加起来只能是45,所以要把三个三位数的个位、十位、百位分离出来,加起来看看是否为45,以及1~9乘起来只能等于362880。两个条件缺一不可,否则答案会出错

思考题:
题目2: 运行结果是 无限循环
目的在于,在定义循环变量时,尽量采用int型及整数的加减,因为循环的本质意义就是 通过各种条件来控制语句重复运行次数,而这个次数本身就是整数,要实现小数的功能尽量通过循环中的语句来实现。

蓝桥杯备考——算法竞赛入门经典(第2版)学习笔记2相关推荐

  1. 算法竞赛入门经典(第二版)第三章习题

    声明:作者水平有限,只是会基础C语言的小菜,C++还未入门.作者仅根据算法竞赛入门经典(第二版)书上第三章习题所述题意而编写,并未严格按照原题的输入输出编写,代码仅经过个人测试(OJ网站太慢了).代码 ...

  2. 刘汝佳《算法竞赛入门经典(第二版)》习题(三)

    刘汝佳<算法竞赛入门经典(第二版)>第三章习题(一) 习题3-1 得分(ACM/ICPC Seoul 2005,UVa1585) 给出一个由O和X组成的串(长度为1~80),统计得分.每个 ...

  3. 刘汝佳《算法竞赛入门经典(第二版)》习题(六)

    刘汝佳<算法竞赛入门经典(第二版)>第四章习题(4-1~4-3) 习题4-1 象棋(Xiangai,ACM/ICPC Fuzhou 2011,UVa1589) 考虑一个象棋残局,其中红方有 ...

  4. 刘汝佳《算法竞赛入门经典(第二版)》习题(二)

    刘汝佳<算法竞赛入门经典(第二版)>第二章习题 目录 刘汝佳<算法竞赛入门经典(第二版)>第二章习题 习题2-1 水仙花数 习题2-2 韩信点兵 习题2-3 倒三角形 习题2- ...

  5. 算法竞赛入门经典 第2版

    算法竞赛入门经典 包括算法竞赛入门经典训练指南.算法竞赛入门经典各章习题答案.算法竞赛入门经典(第二版) 链接:https://pan.baidu.com/s/1O-bGyhdCqYtRvSBpn7J ...

  6. 《算法竞赛入门经典(第2版)》

    <算法竞赛入门经典(第2版)> 基本信息 作者: 刘汝佳 丛书名: 算法艺术与信息学竞赛 出版社:清华大学出版社 ISBN:9787302356288 上架时间:2014-6-5 出版日期 ...

  7. 《算法竞赛入门经典(第二版)》pdf

    下载地址:网盘下载 内容简介  · · · · · · <算法竞赛入门经典(第2版)>是一本算法竞赛的入门与提高教材,把C/C++语言.算法和解题有机地结合在一起,淡化理论,注重学习方法和 ...

  8. 《算法竞赛入门经典(第2版)》——学习记录

    前言:   这里主要记录本人在学习紫书过程中充分理解过的题目的AC代码,便于以后回顾时查找代码和思路,毕竟看别人的真的有点难懂.此外,本书甚至是本书之外的相关知识学习也可能在此留下记录.   作为一只 ...

  9. [刷题]算法竞赛入门经典(第2版) 4-3/UVa220 - Othello

    书上具体所有题目:http://pan.baidu.com/s/1hssH0KO 代码:(Accepted,0 ms) //UVa 220 - Othello #include<iostream ...

最新文章

  1. 以太网,IP,TCP,UDP数据包分析【转】
  2. MATLAB应用实战系列(四十三)-基于MATLAB的光伏并网系统仿真设计
  3. Discuz! 在线中文分词、关键词提取服务
  4. 贪心(数据结构):COGS 468. [NOI2010]超级钢琴
  5. Linux用户:您上一次使用Windows已有多长时间了?
  6. 关于技术的学习及批判
  7. 通俗易懂的MonteCarlo积分方法(七)
  8. vue实现搜索框搜索新增_基于Vue el-autocomplete 实现类似百度搜索框功能_含真_前端开发者...
  9. Windows server 2012体验之活动目录回收站
  10. 小马哥杂牌机高仿机刷机教程---史上最简单的卡刷方法。adb推送模式自动卡刷教程
  11. css的外链写法,纯CSS代码为外链增加图标
  12. MY1690-12P语音模块实现特定的语音提示功能
  13. 计算机游戏如何产生娱乐效果,浅谈电脑游戏与信息技术教学
  14. 云端守望者(下):十八般武艺
  15. 系统动力学专拓考试重点总结
  16. 重装系统电脑黑屏开不了机如何处理
  17. Webpack:Loader学习—— Pitching Loader
  18. 语义分割(Semantic Segmentation)方法
  19. 数字签名与数字证书技术简介(二)
  20. 分别解释final,finally,finalize是什么?

热门文章

  1. 阿Q:被恶心的虫子咬了
  2. 机器视觉 Histogram of oriented gradients
  3. 【源码+演示】高考加油!HTML+CSS特效文字祝福_程序员祝福高考学子旗开得胜!
  4. 设计模式 适配器模式 以手机充电器为例
  5. 简评搜狗输入法(ios端)
  6. 哈哈哈哈 (外一则)
  7. C# Stopwatch 计算程序所有时间
  8. java毕业设计宠物销售管理系统Mybatis+系统+数据库+调试部署
  9. 女生学化工工艺好和计算机,哪些艺术型专业适合理科女生报考
  10. 酷睿i5 1340p和i5 13500h差距 i51340p和13500h区别