第2章资料C语言第二章.docx
《第2章资料C语言第二章.docx》由会员分享,可在线阅读,更多相关《第2章资料C语言第二章.docx(44页珍藏版)》请在冰豆网上搜索。
第2章资料C语言第二章
2.4小结与习题
2.4.1输出技巧
首先是输出技巧。
通过对程序2-1进行小小的改动来实现输出2,4,6,8,…,2n,每个一行。
为了方便,现把程序复复制如下:
1#include
2intmain()
3{
4inti,n;
5scanf("%d",&n);
6for(i=1;i<=n;i++)
7printf("%d\n",i);
8return0;
9}
任务1:
修改第7行,不修改第6行。
解答:
修改第7行的如下:
7printf("%d\n",2*i);
任务2:
修改第6行,不修改第7行。
解答:
修改第6行的如下:
6for(i=2;i<=2*n;i+=2)
2.4.2浮点数陷阱
“!
=”运算符表示“不相等”,则下面的程序的运行结果是什么?
#include
intmain()
{
doublei;
for(i=0;i!
=10;i+=0.1)
printf("%.1lf\n",i);
return0;
}
说明:
对于i可以达到10.0,但永远不会与10相等,所以for循环是一个死循环。
对于float和dobule类型的数据不能直接用==和!
=来比较大小,即不要测试精确的浮点型数值,需要用精度比较,来测试一个可接受的数值范围。
如:
for(i=0;fabs(10-i)>1e-5;i+=0.1)
2.4.5小结
现在对本章介绍的循环总结如下:
(1)介绍了在编程中的一些经验,如使用计算器、累加器、以及“当前最小/最大值”这样的中间变量。
使用printf输出一些关键的中间变量能有效地帮助我们了解程序执行过程、发现错误。
(2)遇到问题需要自己分析和设计,可以先写出“伪代码”,然后转换成程序。
伪代码是为了让思路更清晰,突出主要矛盾。
(3)编写好程序后,测试显得相当重要。
如前面的例子中几乎都有陷阱:
运算结果溢出、运算时间过长等。
(4)再次强调:
编程不是看书看会的,也不是听课听会的,而是练会的。
2.4.6上机练习
习题2-1位数(digit)
输入一个不超过109的正整数,输出它的位数。
例如12735的位数是5。
请不要使用任何数学函数,只用四则运算和循环语句实现。
【分析】
采取的策略是不断地整除10,检查商是否大于10。
程序如下:
习题2-2水仙花数(daffodil)
输出100~999中的所有水仙花数。
若3位数ABC=A2+B2+C2,则称其为水仙花数。
例如153=13+53+33,所以153是水仙花数。
【分析】
采取的策略是穷举100~999之内的所有数据,检查是否满足题目的要求。
程序如下:
/*ShineCheng注:
以下程序a,b,c变量名字取的不是很好,建议大家用更清晰的方法,如bai,shi,ge。
*/
#include
intmain(){
intn,a,b,c;/*n的百、十、个位*/
for(n=100;n<=999;n=n+1){
a=n/100;
b=(n-a*100)/10;/*也可以写成b=(n%100)/10;*/
c=n%10;
if(n==a*a*a+b*b*b+c*c*c)/*算术运算的优先级要高于逻辑运算*/
printf("%d\n",n);
}
Return0;
}
习题2-3韩信点兵(hanxi)
相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。
输入3个非负整数a,b,c,表示每种队形排尾的人数(a<3,b<5,c<7),输出总人数的最小值(或报告无解)。
已知总人数不小于10,不超过100。
样例输入:
216
样例输出:
41
样例输入:
213
样例输出:
Noanswer
【分析】
采取的策略是用穷举法解题;总人数(10,100]。
程序如下:
#include
intmain(){
inta,/*三人一排,余a人*/
b,/*五人一排,余b人*/
c,/*七人一排,余c人*/
sum;/*总人数*/
scanf("%d%d%d",&a,&b,&c);
for(sum=10;sum<=100;sum=sum+1){
if((sum%3==a)&&(sum%5==b)&&(sum%7==c)){
printf("%d",sum);
break;
}
}
if(sum>100)printf("Noanswer");
return0;
}
习题2-4倒三角形(triangle)
输入正整数n≤20,输出一个n层的倒三角形。
例如n=5时输出如下:
#########
#######
#####
###
#
【分析】
该图形一共有n行,这次要考虑每行中,先输出若干个空格,所以,其外循环为:
for(i=1;i<=n;i++){
输出若干个空格
输出若干#
换行
}
下面列出在第i行,其空格数,#字符数与i的关系为:
行i空格数#字符数
102n-1
212n-3
…
ii-12n-2i+1
…
n-1n-23
nn-11
即第i行的空格数为i-1个,#字符个数为2n-2i+1。
#字符个数为2n-2i+1是如何得来的呢?
如果将行从第n行记为第1行,第n-1行记为第2行,…,则原第i行现在记为第n-i+1行。
现在行号为第1行的#字符的个数为1,现在行号为第2行的#字符的个数为3=2*2-1,所以原第i行现在记为第n-i+1行,输出的#字符的个数为2(n-i+1)-1=2n-2i+1。
所以在第i行输出空格和输出#字符的内循环分别为:
for(j=1;j<=i-1;j++)
printf("");
for(j=1;j<=2n-2i+1;j++)
printf("%c",'#');
合起来,构成一个完整程序。
程序如下:
#include
intmain(){
intn,/*输出n行;n<=20*/
i,/*打印第i行*/
j;
scanf("%d",&n);
for(i=1;i<=n;i=i+1){
/*在第i行,打印(i-1)个空格*/
for(j=1;j<=i-1;j=j+1)printf("");
/*在第i行,打印(2*n-2*i+1)个#*/
for(j=1;j<=(2*n-2*i+1);j=j+1)printf("#");
printf("\n");/*输出结束后换行,否则所有的#号在同一行输出*/
}
return0;
}
引申1:
用for循环编程画出下列图形:
M
MM
MMM
MMMM
MMMMM
MMMMMM
MMMMMMM
MMMMMMMM
MMMMMMMMM
MMMMMMMMMM
【分析】
该图形一共有10行,每一行增加一个字符,所以,应循环10次,每次输出一行,其循环模式为:
for(i=1;i<=10;i++){
输出第i行
换行
}
“输出第i行”是在for循环中的一个小循环。
每次执行“输出第i行”,其长度都是不一样的,但长度的变化正好与循环变量i同步,故可以依赖于i。
注意到第i行的M字符数与i的关系。
行iM数
111
222
333
…
101010
所以,可以得到“输出第i行”的循环为:
for(j=1;j<=i;j++)printf(“%c”,’M’)
完整的程序如下:
#include
intmain(){
inti,j;
for(i=1;i<=10;i++){
for(j=1;j<=i;j++)
printf("%c",'M');
printf("\n");
}
return0;
}
说明:
对付这种字符图形,一般用两重循环,外循环遍历所有行,内循环遍历行中每个字符。
引申2:
输出下列图形:
MMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMM
MMMMMMMMMMMMM
MMMMMMMMMMM
MMMMMMMMM
MMMMMMM
MMMMM
MMM
M
【分析】
该图形一共有10行,这次要考虑每行中,先输出若干个空格,所以,其外循环为:
for(i=1;i<=10;i++){
输出若干个空格
输出若干M
换行
}
下面列出在第i行,其空格数,M数与i的关系为:
行iM数
1019
2117
3215
4313
…
1091
即第i行的空格数为i-1个,M个数为21-2i。
即在第i行输出空格和输出M字符的内循环分别为:
for(j=1;j<=i-1;j++)
printf(“”);
for(k=1;k<=21-2i;k++)
printf(“%c”,’M’);
合起来,构成一个完整程序。
完整程序如下:
#include
intmain()
{
inti,j,k;
for(i=1;i<=10;i++)
{
for(j=1;j<=i-1;j++)
printf("");
for(k=1;k<=21-2*i;k++)
printf("%c",'M');
printf("\n");
}
return0;
}
引申3:
输出下列图形:
A
ABC
ABCDE
ABCDEFG
ABCDEFGHI
ABCDEFGHIJK
ABCDEFGHIJKLM
ABCDEFGHIJKLMNO
ABCDEFGHIJKLMNOPQ
ABCDEFGHIJKLMNOPQRS
【分析】
该图形一共有10行,这次要考虑每行中,先输出若干个空格,所以,其外循环为:
for(i=1;i<=10;i++){
输出若干个空格
输出若干字符
换行
}
如果要输出A起头依序的n(n<27)个字母,可以为:
for(ch='A';ch<'A'+n;++ch)
printf("%c",ch);
下面分析每一行中的空格数与字符数与第i行之间的关系:
行iM数
191
283
375
467
…
10019
即第i行的空格数据为10-i个,字符数为2i-1。
因此,输出空格数和字符数的内循环分别为:
for(j=1;j<=10-i;++j)/*输出空格数*/
printf("");
for(ch='A';ch<'A'+2*i-1;++ch)
printf("%c",ch);
合起来,构成一个完整程序。
完整程序如下:
#include
intmain()
{
inti,j;
charch;
for(i=1;i<=10;i++)
{
for(j=1;j<=10-i;++j)/*输出空格数*/
printf("");
for(ch='A';ch<'A'+2*i-1;++ch)/*输出字符*/
printf("%c",ch);
printf("\n");
}
return0;
}
引申4:
输出下列图形:
*
***
*****
*******
*****
***
*
上面的图形可以分成两部分:
(1)
*
***
*****
*******
与引申3的分析一样,输出空格数和字符数的内循环分别为:
for(j=1;j<=4-i;++j)/*输出空格数*/
printf("");
for(k=1;k<=2*i-1;++k)/*输出“*”号*/
printf("*");
(2)
*****
***
*
与引申2的分析一样,输出空格数和字符数的内循环分别为:
for(j=1;j<=i;++j)/*输出空格数*/
printf("");
for(k=1;k<=7-2*i;++k)/*输出“*”号*/
printf("*");
合起来,构成一个完整程序。
完整程序如下:
#include
intmain()
{
inti,j,k;
for(i=1;i<=4;i++)
{
for(j=1;j<=4-i;++j)/*输出空格数*/
printf("");
for(k=1;k<=2*i-1;++k)/*输出“*”号*/
printf("*");
printf("\n");
}
for(i=1;i<=3;i++)
{
for(j=1;j<=i;++j)/*输出空格数*/
printf("");
for(k=1;k<=7-2*i;++k)/*输出“*”号*/
printf("*");
printf("\n");
}
return0;
}
习题2-6调和级数(harmony)
输入一个正整数n,输出H(n)=
的值,保留3位小数。
例如n=3时答案为1.833。
【分析】
本题主要考察循环语句的使用。
秘诀是要从绝对值最小的项开始向绝对值大的项累加。
程序如下:
#include
intmain(){
unsignedintn;/*我们认为int已经足够表达所谓的“正整数”范围*/
doublesum=0;
scanf("%u",&n);
for(;n>=1;n=n-1){
sum=sum+1.0/n;
}
printf("%.3f",sum);
return0;
}
习题2-7近似计算(approximation)
计算
,直到最后一项小于10-6。
【分析】
本题主要考察循环语句的使用。
秘诀是要从绝对值最小的项开始向绝对值大的项累加。
程序如下:
#include
intmain(){
intn;
doublesum=0;
intsymbol;
n=1000001;/*显然,1/1000001小于0.000001*/
for(;n>=1;n=n-2){
if((n+1)/2%2==1)symbol=1;
elsesymbol=-1;
sum=sum+symbol*(1.0/n);
}
printf("%.20lf",sum);
return0;
}
习题2-8子序列的和(subsequence)
输入两个正整数n,保留5位小数。
例如,n=2,m=4时答案是0.42361;n=65536,m=655360时答案为0.00001。
注意:
本题有陷阱。
【分析】
本题主要考察循环语句的使用。
秘诀是要从绝对值最小的项开始向绝对值大的项累加。
所谓的陷阱,是指m的平方可能会导致整数溢出。
所以,另一个秘诀是,把m和n定义为double。
程序如下:
#include
intmain(){
doublem,n;
doublesum=0;
scanf("%lf%lf",&n,&m);/*假设用户输入的n总是不大于m*/
for(;m>=n;m=m-1){
sum=sum+1.0/(m*m);/*注意,不能写成sum=1/m*m*/
}
printf("%.5lf",sum);
return0;
}
习题2-9分数化小数(decimal)
输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。
a,b≤106,c≤100。
例如a=1,b=6,c=4时应输出0.1667。
【分析】
注意到a,b,c均为正整数,所以输出结果的小数点后至少会有1位。
另外,从本题的示例输出中可以感知到,小数的最后一位,是要考虑到四舍五入进位的。
采取的策略是模拟整数除法的过程,利用整除所得的余数,不断地对目标结果求精。
程序如下:
#include
intmain(){
inta,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("%d.",a/b);//先输出整数和小数点
for(inti=1;i<=c;i++)//按照除法规则进行相除运算
{
a=(a%b)*10;//确定下次被除数
if(i!
=c)
printf("%d",a/b);
else{//确定第c位小数是否需要进行四舍五入
a=(a%b)*10;
if(a/b>=5)
printf("%d",a/b+1);
else
printf("%d",a/b);
}
}
printf("\n");
return0;
}
2.4.7ACM题目中的输入输出
1.ACM题目特点
由于ACM竞赛题目的输入数据和输出数据一般有多组(不定),并且格式多种多样,所以,如何处理题目的输入输出是对大家的一项最基本的要求。
这也是困扰初学者的一大问题。
经常有刚接触ACM在线测试系统的同学抱怨:
“为什么我在OJ(OnlineJudge)上连简单的A+B也通不过?
”
先给一个竞赛样例:
题目名称:
A+BforInput-OutputPractice(I)
链接地址:
TimeLimit:
1Seconds MemoryLimit:
32768K
ProblemDescription
YourtaskistoCalculatea+b.
Tooeasy?
!
Ofcourse!
Ispeciallydesignedtheproblemforacmbeginners.
Youmusthavefoundthatsomeproblemshavethesametitleswiththisone,yes,alltheseproblemsweredesignedforthesameaim.
Input
Theinputwillconsistofaseriesofpairsofintegersaandb,separatedbyaspace,onepairofintegersperline.
Output
Foreachpairofinputintegersaandbyoushouldoutputthesumofaandbinoneline,andwithonelineofoutputforeachlineininput.
SampleInput
15
1020
SampleOutput
6
30
初学者很常见的一种写法:
#include
intmain()
{
inta,b;
scanf(“%d%d”,&a,&b);
printf(“%d”,a+b);
return0;
}
但上述代码提交上述到OJ上不能通过,是什么原因呢?
这就是下面需要解决的问题。
下面来分类进行介绍。
2.基本输入
(1)第一类输入
输入不说明有多少个InputBlock,以EOF为结束标志,例如上面的例子。
源代码如下:
#include
intmain()
{
inta,b;
while(scanf("%d%d",&a,&b)!
=EOF)printf("%d\n",a+b);
}
本类问题的输入方案为:
①C语法
while(scanf("%d%d",&a,&b)!
=EOF)
{
....
}
②C++语法
while(cin>>a>>b)
{
....
}
说明:
①scanf函数返回值就是读出的变量个数,如:
scanf("%d%d",&a,&b);,如果只有一个整数输入,返回值是1;如果有两个整数输入,返回值是2;如果一个都没有,则返回值是-1。
②EOF是一个预定义的常量,等于-1。
(2)第二类输入
输入一开始就会说有n个InputBlock,下面接着是输入n个InputBlock。
参见:
HDOJ_1090(
源代码如下:
#include
intmain()
{
intn,i,a,b;
scanf("%d",&n);
for(i=0;i{
scanf("%d%d",&a,&b);
printf("%d\n",a+b);
}
}
本类问题的输入方案为:
①C语法
scanf("%d",&n);
for(i=0;i{
....
}
②C++语法
cin>>n;
for(i=0;i{
....
}
(3)第三类输入
输入不说明有多少个InputBlock,但以某个特殊输入为结束标志。
参见:
HDOJ_1091(
源代码如下:
#include
intmain()
{
inta,b;
while(scanf("%d%d",&a,&b)&&(a!
=0&&b!
=0))
printf("%d\n",a+b);
}
上面程序有什么问题?
本类问题的输入方案为:
①C语法
while(scanf(“%d”,&n)!
=EOF&&n!
=0)
{
....
}
②C++语法
while(cin>>n&&n!
=0)
{
....
}
(4)第四类输入
以上几种情况的组合,可以参照如下网页,进行上机实验。
①
②
③
(5)第五类输入
输入一整行的字符串。
参见:
HDOJ_1048(http: