[算法竞赛入门]第一章_算法概述
1 第1部分 语 言 篇2 3 第1章 程序设计入门4 【学习内容相关章节】5 1.1算术表达式 1.2变量及其输入 1.3顺序结构程序设计6 1.4分支结构程序设计 1.5C/C++编码规范 1.6小结与习题 7 【学习目标】8 (1)熟悉C语言程序的编译和运行;9 (2)学会编程计算并输出常见的算术表达式的结果;10 (3)掌握整数和浮点数的含义和输出方法;11 (4)掌握数学函数的使用方法;12 (5)初步了解变量的含义;13 (6)掌握整数和浮点数变量的声明方法;14 (7)掌握整数和浮点数变量的读入方法;15 (8)掌握变量交换的三变量法;16 (9)理解算法竞赛中的程序三步曲:输入、计算、输出;17 (10)记住算法竞赛的目标及其对程序的要求。18 19 【学习要求】20 掌握算术表达式的书写格式、整数和浮点数的声明、输入和输出方法,C语言中scanf的输入格式和printf的输出格式。21 【学习内容提要】22 计算机速度快,很适合做计算和逻辑判断工作。本章首先介绍顺序结构程序设计,其基本思路是:把需要计算机完成的工作分成若个步骤,然后依次让计算机执行。这部分的重点是计算,所以要求掌握算述表达式的书写格式,整数和浮点数的输入和输出方法。由于是竞赛,所以还要掌握C语言中scanf的输入格式和printf的输出格式中的一些特殊的格式。23 接下来介绍分支结构程序设计,用到了逻辑判断,根据不同情况执行不同语句。24 【学习重点、难点】25 学习重点:26 (1)掌握算术表达式的书写格式;27 (2)整数和浮点数的声明、输入和输出方法;28 (3)C语言中scanf的输入格式和printf的输出格式。29 学习难点:30 整数和浮点数的声明、输入和输出方法,scanf的输入格式和printf的输出格式。31 【课时安排(共2学时)】32 1.1算术表达式(0.25学时) 1.2变量及其输入(0.25学时) 33 1.3顺序结构程序设计(0.5学时) 1.4分支结构程序设计(0.5学时) 34 1.5C/C++编码规范(自学) 1.6小结与习题 (0.5学时)35 36 37 38 1.1算术表达式39 40 计算机的“本职”工作是计算,从算术表达式入手,分析计算机是如何进行复杂的计算。下面来看一个完整的程序1-1。41 程序1-1 计算并输出1+2的值42 #include <stdio.h>43 int main(){44 printf("%d\n", 1+2);45 return 0;46 }47 48 程序1-1的功能是计算1+2的值,并把结果3输出到屏幕。49 下面做4个实验:50 (1)实验1:修改程序1-1,输出3-4的结果51 解答:用3-4代替程序1-1的背景为灰色的部分,输出结果为-1。52 (2)实验2:修改程序1-1,输出5×6的结果53 解答:用5*6代替程序1-1的背景为灰色的部分,输出结果为30。54 (3)实验3:修改程序1-1,输出8÷4的结果55 解答:用8/4代替程序1-1的背景为灰色的部分,输出结果为2。56 (4)实验4:修改程序1-1,输出8÷5的结果57 解答:用8/5代替程序1-1的背景为灰色的部分,输出结果为1。58 注意:在C语言中,8/5的确切的含义是8除以5所得的商值的整数部分。59 60 下面来看一个完整的程序1-2。61 程序1-2 计算并输出8/5的值,并保留小数点后1位62 #include <stdio.h>63 int main(){64 printf("%.1lf\n", 8.0/5.0);65 return 0;66 }67 程序1-2的功能是计算8.0/5.0的值,并把结果1.6输出到屏幕。68 注意:在程序1-2的背景为灰色部分中,百分号后面是小数点,然后是数字1,再然后是小写字母l,最后是小写它f。69 下面再来做3个实验:70 (5)实验5:把%.1lf中的数字1改为2,结果如何?能猜想出“1”的确切意思吗?如果把小数点和1都删除,%1lf的含义是什么?71 解答:%lf表示输出double浮点数,如果程序1-2中的printf语句改为printf("%lf\n", 8.0/5.0);,则输出结果为1.600000。72 %.llf表示输出double浮点数,并且小数点后面保留一位数字,所以程序1-2的输出结果为1.6。%.2lf表示输出double浮点数,并且小数点后面保留二位数字。73 (6)实验6:字符串%.llf不变,把8.0/5.0改成原来的8/5,结果如何?74 解答:在VC中调试的输出结果为0.000000。在TC中调试,会出现一个提示“printf:75 floating point formats not linked。Abnormal program termination”。76 (7)实验7:字符串%.1lf改为原来的%d,8.0/5.0不变,结果如何?77 解答:在VC中调试的输出结果为-1717986918。在TC中调试的输出结果为-26214。78 对于上面的实验6和实验7的答案很难简单的解释,真正的原因是涉及整数和浮点编码。79 提示1-1:整数值用%d,实数用%lf输出。80 提示1-2:整数/整数=整数,浮点数/浮点数=浮点数。81 82 算术表达式可以和数学表达式一样复杂,例如计算数学表达式83 的值:84 程序1-3 复杂的表达式计算85 #include <stdio.h>86 #include <math.h>87 int main(){88 printf("%.8lf\n", 1+2*sqrt(3)/(5-0.1));89 return 0;90 }91 说明:92 (1)整数-浮点数是整数先“变”成浮点数,然点浮点数-浮点数=浮点数。93 (2)在程序1-3中用到数学函数sqrt。数学函数sqrt(x)的作用是计算x的算术平方根。一般来说,只要在程序中用到了数学函数,就需要在程序最开始的地方包含文件math.h94 95 96 1.2变量及其输入97 98 在程序中可以通过键盘输入,然后根据输入内容来计算结果。程序如下:99 程序1-4 A+B问题 100 #include <stdio.h> 101 int main(){ 102 int a, b; 103 scanf("%d%d", &a, &b); 104 printf("%d\n", a+b); 105 return 0; 106 } 107 108 说明: 109 (1)变量用来存储可变的数据,它像一个筐,什么都往里面装。它只能用来存储事先指定的数据结构。 110 (2)在scanf语句中,变量a和b前面的&(取地址)符号,不能丢掉。 111 提示1-3:scanf中的占位符和变量的数据类型应一一对应,且每个变量前需要&符号。 112 113 下面来看一个复杂一点的例子。 114 例1-1 圆柱体的表面积。 115 输入底面积半径r和高h,输出圆柱体的表面积,保留3位小数,格式见样例。 116 样例输入:3.5 9 117 样例输出:Area=274.889 118 【分析】 119 圆柱体的表面积=底面积×2+侧面积。根据平面几何知识,底面积=πr2,侧面积=2πrh。完整的程序如下: 120 程序1-5 圆柱体的表面积 121 #include <stdio.h> 122 #include <math.h> 123 int main(){ 124 const double pi=4.0*atan(1.0); 125 double r,h,s1,s2,s; 126 scanf("%lf%lf",&r,&h); 127 s1=pi*r*r; 128 s2=2*pi*r*h; 129 s=s1*2.0+s2; 130 printf("Area =%.3lf\n",s); 131 return 0; 132 } 133 说明: 134 (1)在程序1-5的语句“const double pi=4.0*atan(1.0);”中,const类型限定修饰符表示把一个对象转换成一个常量(constant),在程序中任何改变这个值的企图都导致编译错误,因此它被称为是只读的(read-only)。由于常量在定义后就不能修改,所以它必须初始化,未初始化的常量定义将导致编译错误。 135 函数atan()计算数的反正切值,返回角度以弧度表示,也就是就是数学中的反正切函数arctg,由于tg(arctg(1))=1,即arctg(1)=π/4,所以atan(1.0)=0.785398(弧度)≈π/4。 136 语句“const double pi=4.0*atan(1.0);”就是将常量pi赋值为4.0*0.785398=3.141 137 593(π的近似值)。 138 (2)const与#define的比较。 139 在C/C++语言中,存在两种符号常量:用#define定义的宏常量和用const定义的常量。但后者比前者具有更多的优点: 140 ①#define是预编译伪指令,它定义的宏常量在进入编译阶段前就已经替换为所代表的字面常量,因此宏常量在本质上是字面常量。 141 const常量有数据类型,而宏常量没有数据类型。编译器可以对它进静态类型安全检查;而对#define常量只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)。 142 所以在C++程序中应尽量使用const来定义符号常量,包括字符串常量。 143 ②有些集成化的调试工具可以const常量进行调试,但是不能对宏常量进行调试。 144 (3)在正规比赛中,题目包含着输入输出格式规定,还有样例数据。 145 (4)在比赛时,选手程序的执行是自动完成的,没有人工干预。不要在用户输入之前打印提示信息,否则会让程序丢掉大量的分数,因为这些提示信息会被当作输出的数据的一部分。 146 (5)不会让程序“按任意键退出”(例如在Dev C++中调用system(“pause”),或者加一个多余的getchar())。所以千万不要在算法竞赛中这样做。 147 提示1-4:在算法竞赛中,输入前不要打印提示信息。输出完毕后应立即终止程序,不要等待用户按键,因为输入输出过程都是自动的,没有人工干预。 148 提示1-5:在算法竞赛中不要使用头文件conio.h,包括getch(),clrscr()。 149 提示1-6:在算法竞赛中,每行输出均应以回车符结束,包括最后一行。除非特别说明,每行的行首不应空格,但行末通常可以有多余空格。另外,输出的每两个数或者字符串之间应以单个空格隔开。 150 提示1-7:尽量用const关键字声明常数。 151 提示1-8:赋值是个动作,先计算右边的值,再赋给左边的变量,覆盖它原来的值。 152 提示1-9:printf的格式字符串中可以包含其他可打印符号,打印时原样输出。 153 154 155 1.3顺序结构程序设计 156 157 例1-2 三位数反转。 158 输入一个三位数,分离出它的百位、十位和个位,反转后输出。 159 样例输入:127 160 样例输出:721 161 【分析】 162 首先将三位数读入变量n,然后进行分离。百位等于n/100(注意这里取的是商的整数部分),十位数等于n/10%10(这里的%是取余数操作),个位数等于n%10。完整的程序如下: 163 程序1-6 三位数反转(1) 164 #include <stdio.h> 165 int main(){ 166 int n; 167 scanf("%d", &n); 168 printf("%d%d%d\n", n%10, n/10%10, n/100); 169 return 0; 170 } 171 注意:程序1-6的输出结果是025。如果一个数的个位是0,例如输入是520,输出结果是025,还是25,在算法竞赛中如果遇到这个问题,可向监考老师询问。 172 提示1-10:算法竞赛的题目应当严密的,各种情况下的输出均应有严格规定。如果在比赛中发现题目有漏洞,应当向相关人员询问,而尽量不要自己随意假定。 173 对程序1-6,如果要输出25,解决办法是在输出结果前把结果存在变量m中,直接用%d格式输出m,这样可以输出25;如果要输出025,把输出格式变为%03d即可。 174 175 程序1-7 三位数反转(2) 176 #include <stdio.h> 177 int main(){ 178 int n, m; 179 scanf("%d", &n); 180 m = (n%10)*100 + (n/10%10)*10 + (n/100); 181 printf("%03d\n", m); 182 return 0; 183 } 184 例1-3 交换变量。 185 输入两个整数a和b,交换二者的值,然后输出。 186 样例输入:824 16 187 样例输出:16 824 188 【分析】 189 按题目的所说,先把变量存入变量a和b,然后交换。最经典的方法是三变量法: 190 程序1-8 变量交换(1) 191 #include <stdio.h> 192 int main(){ 193 int a, b, t; 194 scanf("%d%d", &a, &b); 195 t = a; 196 a = b; 197 b = t; 198 printf("%d %d\n", a, b); 199 return 0; 200 } 201 提示1-11:赋值a=b之后,变量a原来的值被覆盖,而b的值不变。 202 另一个方法没有借助任何变量,但较难理解: 203 程序1-9 变量交换(2) 204 #include <stdio.h> 205 int main(){ 206 int a, b; 207 scanf("%d%d", &a, &b); 208 a = a + b; 209 b = a - b; 210 a = a - b; 211 printf("%d %d\n", a, b); 212 return 0; 213 } 214 说明:程序1-9的功能也是交换两个变量的值(少用了一个中间变量来实现),但实际上很少使用,因为它的适用范围很窄:只有定义了加减法的数据类型才能这么做。 215 提示1-12:交换两个变量的三变量法适用范围广,推荐使用。 216 多数算法竞赛采用黑盒测试,即只考查程序解决问题的能力,而不关心它采用的方法,所以三变量法不是解决变量交换的最佳途径,对于本题而言,最合适程序如下: 217 程序1-10 变量交换(3) 218 #include <stdio.h> 219 int main(){ 220 int a, b; 221 scanf("%d%d", &a, &b); 222 printf("%d %d\n", b, a); 223 return 0; 224 } 225 换句话说,我们的目标是解决问题,而不是为了写程序而写程序,同时应保持简单(Keep It Simple and Stupid,KISS),而不是自己创造条件去展示编程序技巧。 226 提示1-13:算法竞赛是在比谁更好地解决问题,而不是在比谁写的程序看上去更高级。 227 228 229 1.4分支结构程序设计 230 231 例1-4 鸡兔同笼。 232 已知鸡和兔的总数量为n,总腿数为m。输入m和n,依次输出鸡的数目和兔的数目。如果无解,则输出“No answer”(不要引号)。 233 样例输入:14 32 234 样例输入:12 2 235 样例输出:10 6 236 样例输出:No answer 237 【分析】 238 设鸡有a只,兔有b只,则a+b=n,2a+4b=m,联立解得a=(4n-m)/2,b=n-a。在本题中,首先,a和b都是整数;其次,a和b必须是非负的。可以通过下面的程序判断: 239 程序1-11 鸡兔同笼 240 #include <stdio.h> 241 int main(){ 242 int a, b, n, m; 243 scanf("%d%d", &n, &m); 244 a = (4*n-m)/2; 245 b = n-a; 246 if(m % 2 == 1 || a < 0 || b < 0) 247 printf("No answer\n"); 248 else 249 printf("%d %d\n", a, b); 250 return 0; 251 } 252 在本程序中,用到if语句,其基本格式如提示1-14所示。 253 提示1-14:if语句的基本格式为:if(条件) 语句1; else 语句2。 254 在程序1-11中,m%2==1||a<0||b<0是一个逻辑表达式。和算术表达式类似,逻辑表达式也由运符符和值构成,例如“||”运算符称为“逻辑或”,a||b表示a和b只要有一个为真,a||b就为真;如果a和b都为真,则a||b也为真。 255 提示1-15:if语句的条件是一个逻辑表达式,它的值可能为真,也可能为假。 256 在逻辑表达式a||b中,只要a为真,无论b的取值为真或假,a||b均为真。换句话说,只要a真,不必计算b的值。C语言正是采取了这样的策略,称为短路(short-circuit)。类似地,逻辑表达式a&&b,也存在这种短路现象,只要a为假,不必计算b的值,结果必为假。 257 提示1-16:C语言中的逻辑运算符都是短路运算符。一旦能够确定整个表达式的值,就在再继续计算。 258 课堂小练习1 写出下列各逻辑表达式的值(真为 1,假为 0),设 a=3, b=4, c=5。 259 (1)a+b>c && b==c 260 答案:由于b==c为假,所以剩下的就不用计算了。 261 (2)a || b+c && b-c 262 答案:1,由于a为真,所以剩下的就不用计算了。 263 (3)!(a>b) && !c || 1 264 答案:1,由于1为真,所以剩下的就不用计算了。 265 (4)!(x=a) && (y=b) && 0 266 答案:0,由于0为假,所以剩下的就不用计算了。 267 (5)!(a+b)+c-1 && b+c/2 268 答案:0,!(a+b)+c-1为假,所以剩下的就不用计算了。 269 例1-5 三整数排序。 270 输入3个整数,从小到大排序后输出。 271 样例输入:20 7 33 272 样例输入:7 20 33 273 【分析】 274 a、b、c3个数一共只有6种可能的顺序:a b c、a c b、b a c、b c a、c a b、c b a,所以最简单的思路是使用6条if语句。 275 程序1-12 三整数排序(1)(错误) 276 #include <stdio.h> 277 int main(){ 278 int a, b, c; 279 scanf("%d%d%d", &a, &b, &c); 280 if(a < b && b < c) printf("%d %d %d\n", a, b, c); 281 if(a < c && c < b) printf("%d %d %d\n", a, c, b); 282 if(b < a && a < c) printf("%d %d %d\n", b, a, c); 283 if(b < c && c < a) printf("%d %d %d\n", b, c, a); 284 if(c < a && a < b) printf("%d %d %d\n", c, a, b); 285 if(c < b && b < a) printf("%d %d %d\n", c, b, a); 286 return 0; 287 } 288 注意:在程序1-12中,如果输入1 1 1将得不到任何输出。所以编写出来的程序,即使通过了题目中给出的样例,程序仍然可能存在问题。 289 提示1-17:算法竞赛的目标是编程对任意输入均得到正确的结果,而不仅是样例数据。 290 对于上面出现的错误,它的解决方案是人为地让6种情况没有交叉:把所有的if改成else if。 291 程序1-13 三整数排序(2) 292 #include <stdio.h> 293 int main(){ 294 int a, b, c; 295 scanf("%d%d%d", &a, &b, &c); 296 if(a <= b && b <= c) printf("%d %d %d\n", a, b, c); 297 else if(a <= c && c <= b) printf("%d %d %d\n", a, c, b); 298 else if(b <= a && a <= c) printf("%d %d %d\n", b, a, c); 299 else if(b <= c && c <= a) printf("%d %d %d\n", b, c, a); 300 else if(c <= a && a <= b) printf("%d %d %d\n", c, a, b); 301 else if(c <= b && b <= a) printf("%d %d %d\n", c, b, a); 302 return 0; 303 } 304 提示1-18:如果有多个并列、情况不交叉的条件需要一一处理,可以用else if语句。 305 另一种思路是把a、b、c这3个变量本身改成a≤b≤c的形式。首先检查a和b的值,如果a>b,则交换a和b(利用前面进过的三变量交换法);接下来检果a和c,最后检查b和c,程序如下: 306 程序1-14 三整数排序(3) 307 #include <stdio.h> 308 int main(){ 309 int a, b, c, t; 310 scanf("%d%d%d", &a, &b, &c); 311 if(a > b) { t = a; a = b; b = t; } 312 if(a > c) { t = a; a = c; c = t; } 313 if(b > c) { t = b; b = c; c = t; } 314 printf("%d %d %d\n", a, b, c); 315 return 0; 316 } 317 注意:程序1-14的检查顺序不可颠倒。例如,先判断a>b,然后b>c,最后a>c是不可以的。假如输入3 2 1,则结果为2 1 3。 318 提示1-19:可以用花括号把若干条语句组成一个整体(复合语句)。这些语句仍然按顺序执行。 319 最后一种思路再次利用了“问题求解”这一目标——它实际上并没有真的进行排序:求出了最小值和最大值,中间值是可以计算出来的。 320 程序1-15 三整数排序(4) 321 #include <stdio.h> 322 int main(){ 323 int a, b, c, x, y, z; 324 scanf("%d%d%d", &a, &b, &c); 325 x = a <? b <? c; 326 x = a; if(b < x) x = b; if(c < x) x = c; 327 z = a; if(b > z) z = b; if(c > z) z = c; 328 y = a + b + c - x - z; 329 printf("%d %d %d\n", x, y, z); 330 return 0; 331 } 332 注意:程序1-15中包含了的“当前最小值”x和“当前最大值”z。它们初始化为a,但是随着“比较”操作的进行而慢慢更新,最后变成真正的最小值和最大值。这个技巧极为实用。 333 提示1-20:在难以一次性求出最后结果时,可以用变量储存“临时结果”,从而逐步更新。 334 335 336 1.5 C/C++编码规范 337 338 一个好的程序,不仅要算法正确,效率高,而且还应该可读性好。所谓程序的可读性,就是程序是否能让人容易读懂。在开发实践中,许多情况下可读性与代码效率同等重要。 339 软件开发是团队工作,接手别人编码的程序并在此基础上进行改进是必不可少的,因此可读性在工程实践中非常重要。即使是自己编写的程序,如果可读性不好,过一段时间需要改进时自己再看,也常会看不懂。 340 如何提高程序的可读性呢?在标识符、书写格式、注释三个方面加以培养,再养成一些好的习惯,就能够有效增强程序的可读性。 341 1.5.1 标识符命名注意事项 342 343 应该对变量、常量以及函数等标识进行适当的命名。好的命名方法使标识符易于记忆且使程序可读性大大提高。 344 对标识符命名的基本要求是,看到标识符就能想起或猜出它是做什么用的。如果名字能体现变量的类型或作用域等性质,当然更好。标识符命名应注意以下几点: 345 (1)标识符号应能提供足够信息以说明其用途。 一定不要怕麻烦而懒得起足够长的变量名,少按几个键省下的时间,和日后自己读程序或别人读你的程序揣摩该变量的作用所花的时间相比,实在微不足道。在没有国标合作的项目中编写程序,如果英语实在不好,可以使用拼音,但不要使用拼音缩写。 346 (2)为全局变理取长的、描述信息多的名字,为局部变量限稍短的名字。 347 (3)名字太长时可以适当采用单词的缩写。但要注意缩写方式一致,要缩写就全部缩写。比如单词Number,如果在某个变量里缩写成了: 348 int nDoorNum; 349 那么最好包含Number单词的变量都缩写成Num。 350 (4)注意使用单词的复数形式。如 351 int nTotalStudents,nStudents; 352 nStudents容易让人理解成代表学生数目,而nStudent含义就不十分明显。 353 (5)对于返回值为真或假的函数,加“IS”前缀如: 354 int IsCanceled(); 355 int isalpha(); //C语言标准库函数 356 BOOL IsButtonPushed();357 1.5.2 程序的书写格式 358 359 书写格式好的程序,看起来才有好心情,谁也不愿意看下面这样的程序: 360 void main() 361 { 362 int t, x, y; 363 cin>>t; 364 while (t>0) 365 { 366 min=60000; 367 cin>>N>>x>>y>>max; plat[0].x1=x; 368 plat[0].x2=x; plat[0].h=y; 369 for (int i=1;i<=N;i++) 370 { 371 cin>>plat[i].x1>>plat[i].x2>>plat[i].h; 372 plat[i].t1=-1; 373 plat[i].t2=-1; 374 if (plat[i].h>y) {i--; N--; } 375 } 376 plat[0].t1=0;plat[0].t2=0; 377 qsort((void*)(&plat[1]), N, sizeof(plat[0]), compare); 378 tryway(0); 379 t--; 380 cout<<min<<endl; 381 } 382 } 383 因此,如果想要让你的程序看起来赏心悦目,应该注意以下几点: 384 (1)正确使用缩进 385 首先,一定要有缩进,否则代码的层次不明显。缩进应为4 个空格较好。需要缩进时一律按Tab 键,或一律按空格键,不要有时用Tab 键缩进,有时用空格键缩进。一般开发环境都能设置一个Tab 键相当于多少个空格,此时就都用Tab 键。 386 (2)行宽与折行 387 一行不要太长,不能超过显示区域,以免阅读不便。太长则应折行,折行最好发生在运算符前面,不要发生在运算符后面。如 388 if(Condition1() && Condition2() 389 && Condition3()) { 390 } 391 3)‘{’, ‘}’位置不可随意放置。 392 建议将‘{’放在一行的右边,而将‘}’单独放置一行。如: 393 if(condition1()) { 394 DoSomething(); 395 } 396 比较 397 if(condition1()) 398 { 399 DoSomething(); 400 } 401 这种写法,前者既不影响可读性,又能节省一行。 402 但是对于函数体或结构定义的的第一个‘{’,还是单独一行更为清晰。 403 (4)变量和运算符之间最好加1个空格,如: 404 int nAge = 5; 405 nAge = 4; 406 if(nAge >= 4) 407 printf(“%d”, nAge); 408 for(i = 0; i < 100; i++);409 1.5.3 注释的写法 410 411 在工程实践中,文件开头,全局变量定义处,函数开头,都应该有注释。 412 文件开头的注释模板如下: 413 /****************************************************************** 414 ** 文件名: 415 ** Copyright (c) 1998-1999 *********公司技术开发部 416 ** 创建人: 417 ** 日 期: 418 ** 修改人: 419 ** 日 期: 420 ** 描 述: 421 ** 422 ** 版 本: 423 **--------------------------------------------------------------------------- 424 ******************************************************************/ 425 函数开头的注释模板如下: 426 /***************************************************************** 427 ** 函数名: 428 ** 输 入: a,b,c 429 ** a--- 430 ** b--- 431 ** c--- 432 ** 输 出: x--- 433 ** x为1, 表示... 434 ** x为0, 表示... 435 ** 功能描述: 436 ** 用到的全局变量: 437 ** 调用模块: 438 ** 作 者: 439 ** 日 期: 440 ** 修 改: 441 ** 日 期: 442 ** 版本 443 ****************************************************************/ 444 本书由于篇幅所限,书中程序略去了文件开始处和函数开始处的注释。 445 1.5.4 一些好的编程习惯 446 447 (1)尽量不要用立即数,而用#define 定义成常量,以便以后修改。例如: 448 #define MAX_STUDENTS 20 449 struct SStudent aStudents [MAX_STUDENTS];比 451 struct SStudent aStudents [20];好。 453 再例如: 454 #define TOTAL_ELEMENTS 100 455 for(i = 0; i < TOTAL_ELEMENTS; i++) { 456 } 457 (2)使用sizeof(),不直接使用变量所占字节数的数值。如应该写成: 458 int nAge; 459 for(j = 0; j < 100; j++ ) 460 fwrite(fpFile, & nAge, 1, sizeof(int)); 461 不应该写: 462 for( j = 0; j < 100; j++ ) 463 fwrite( fpFile, & nAge, 1, 4); 464 (3)稍复杂的表达式中要积极使用括号,以免优先级理解上的混乱以及二义性。 465 n = k++ + j; //不好 466 n = (k++) + j; //好一点 467 (4)不很容易理解的表达式应分几行写: 468 n = (k++) + j;应该写成: 469 n = k + j; 470 k++; 471 (5)嵌套的if else 语句要多使用 { } 472 if(Condition1()) 473 if(condition2()) 474 DoSomething(); 475 else 476 NoCondition2(); 477 不够好,应该: 478 if(Condition1()) { 479 if(condition2()) 480 DoSomething(); 481 else 482 NoCondition2(); 483 } 484 (6)单个函数的程序行数最好不要超过100 行(两个屏幕高)。 485 (7)尽量使用标准库函数。 486 (8)不要随意定义全局变量,尽量使用局部变量。 487 (9)保持注释与代码完全一致,改了代码别忘改注释。 488 (10)循环、分支层次最好不要超过5层。 489 (11)注释可以与语句在同一行,也可以在上行。 490 (12)一目了然的语句不加注释。 491 492 493 1.6 小结与习题 494 495 通过前几个小节的学习,对顺序结构程序设计和分支程序设计的核心概念和方法,然而对这些进行知识进行总结,并且完成适当的练习是很有必要的。 496 1.6.1 数据类型实验 497 498 实验A1:表达式11111*11111的值是多少?把5个1改为6个1呢?9个1呢? 499 解答:(1)计算表达式11111*11111的值 500 #include <stdio.h> 501 int main(){ 502 printf("11111*11111=%ld",11111*11111); 503 return 0; 504 } 505 程序的运行结果为11111*11111=123454321。 506 (2)计算表达式111111*111111的值 507 只须将(1)中的printf语句改为printf("111111*111111=%ld",111111*111111);,程序的运行结果为111111*111111=-539247567。正确的结果应为12345654321,由于这个数比较大,所以产生了溢出。 508 (3)计算表达式111111111*111111111的值 509 只须将(1)中的printf改为printf("111111111*111111111=%ld ",111111111*1111111 510 11);,程序的运行结果为111111111*111111111=1653732529。正确的结果应为12345678987 511 654321,由于这个数比较大,所以产生了溢出。 512 实验A2:把实验A1中的所有数换成浮点数,结果如何? 513 解答:(1)计算表达式11111.0*11111.0的值 514 #include <stdio.h> 515 int main(){ 516 printf("11111.0*11111.0=%lf",11111.0*11111.0); 517 return 0; 518 } 519 程序的运行结果为11111*11111=123454321.000000。 520 (2)计算表达式111111.0*111111.0的值 521 只须将(1)中的printf语句改为printf("11111*11111=%lf",11111*11111);,程序的运行结果为111111.0*111111.0=12345654321.000000。 522 (3)计算表达式111111111.0*111111111.0的值 523 只须将(1)中的printf改为printf("111111111.0*111111111.0=%lf ",111111111.0 524 *111111111.0);,程序的运行结果为111111111.0*111111111.0=12345678987654320.00000 525 0。 526 实验A3:表达式sqrt(-10)的值是多少?尝试用种方法输出。在计算过程中系统会报错吗? 527 解答: 528 #include <stdio.h> 529 #include <math.h> 530 int main(){ 531 printf("sqrt(-10)=%lf",sqrt(-10)); 532 return 0; 533 } 534 程序的运行结果为sqrt(-10)=-1.#IND,没有报错,但结果异常。 535 实验A4:表达式1.0/0.0、0.0/0.0的值是多少?尝试用种方法输出。在计算过程中系统会报错吗? 536 解答: 537 #include <stdio.h> 538 int main(){ 539 printf("1.0/0.0=%lf 0.0/0.0=%lf",1.0/0.0,0.0/0.0); 540 return 0; 541 } 542 输出结果为1.0/0.0=1.#INF 0.0/0.0=-1.#IND。在计算过程中系统会报错,提示信息为“divdide or mod by zero”。 543 实验A5:表达式1/0的值是多少?在计算过程中系统会报错吗? 544 解答:表达式1/0的值无结果。在计算过程中系统会报错,提示信息为“divdide or mod by zero”。545 1.6.2 scanf输入格式实验 547 scanf("%d%",&a,%b);语句用来输入两个数,可用Tab、空格、回车,作为分隔符。 548 实验B1:在同一行中输入12和2,并以空格分隔,是否得到了预期的效果? 549 解答: 550 #include <stdio.h> 551 void main() { 552 int a,b; 553 scanf("%d%d",&a,&b); 554 printf("%d, %d\n",a,b); 555 return; 556 } 557 从键盘上输入:12 2↙,可以达到输入两个数给变量a和b的效果。 558 实验B2:在不同的两行中输入12和2,是否得到了预期的效果? 559 解答: 560 从键盘上输入:12↙ 561 2↙,也可以达到输入两个数给变量a和b的效果。。 562 实验B3:在实验B1和B2中,在12和2的前面和后面加入大量的空格或水平制表符(TAB),甚至插入一些空行。 563 解答:从键盘上输入的12和2,也可以前面和后面加入大量的空格或水平制表符(TAB),甚至插入一些空行,达到输入两个数给变量a和b的效果。 564 实验B4:把2换成字符s,重复实验B1~B3。 565 解答:从键盘上输入:12 2↙,而输出的结果为12 -85899360。566 1.6.3 printf语句输出实验 568 在printf语句中的格式字符串,决定了数据的输入/输出格式。 569 实验C1:仅用一条printf语句,打印1+2和3+4,用两个空行隔开。 570 解答: 571 #include <stdio.h> 572 void main() { 573 printf("%d\n\n%d",1+2,3+4); 574 return; 575 } 576 实验C2:试着把%d中的两个字符(百分号和小写字)。 577 解答: 578 #include <stdio.h> 579 void main() { 580 printf("%%d\n"); 581 return; 582 } 583 实验C3:试着把\n中的两个字符(反斜线和小写字母n)输出到屏幕。 584 解答: 585 #include <stdio.h> 586 void main() { 587 printf("\\n"); 588 return; 589 } 590 实验C4:像C2、C3那样也需要“特殊方法”才能输出的东西还有哪些?哪些是printf函数引起的问题,哪些不是? 591 解答:输出单引号和双引号需要用\’和\"。 592 1.6.4 测试你的实践能力 593 594 问题1:int型整数的最小值和最大值是多少?(需要精确度)。 595 解答:(1)方法一 596 #include <stdio.h> 597 #include <math.h> 598 int main() { 599 int a,b; 600 a=-pow(2,31); 601 b=pow(2,31)-1; 602 printf("%d %d\n",a,b); 603 return 0; 604 } 605 注意:在TC中int型整数(2字节)的最小值和最大值分别为-32768和32767;在VC6.0中int型整数(4字节)的最小值和最大值分别为-2147483648和2147483647,所以不同的编译系统为整型数据分配的字节数是不相同的。本题程序是在VC6.0中调试的。 606 (2)方法二 607 #include <stdio.h> 608 #include <limits.h> 609 int main() { 610 int a,b; 611 a=INT_MAX; 612 b=INT_MIN; 613 printf("%d %d\n",a,b); 614 return 0; 615 } 616 说明:头文件limits.h中定义了用于表示整数类型大小的常量(宏定义)。 617 (1)如果在C:盘上有TC,就可以在“c:\TC”用记事本打开头文件limits.h,可以找到“#define INT_MAX 0x7FFF”和“#define INT_MIN ((int)0x8000)”。INT_MAX表示在TC中表示最大整数,0x7FFF就是32767;INT_MIN表示在TC中表示最小整数,(int)0x8000就是-32768。 618 (2)如果在C:盘装有VC6.0,就可以在“C:\Program Files\Microsoft Visual Studio\VC98\Include”用记事本打开头文件limits.h,可以找到“#define INT_MIN (-2147483647-1) /* minimum (signed) int value */”和“#define INT_MAX 2147483647 /* maximum (signed) int value */”。INT_MAX表示在VC6.0中表示最大整数是2147483647;INT_MIN表示在TC中表示最小整数是-2147483648。 619 问题2:double型浮点数能精确到多少位小数?或者,这个问本身值得商榷? 620 解答: 621 #include <stdio.h> 622 int main() { 623 double a; 624 a=123.0; 625 printf("%lf\n",a); 626 return 0; 627 } 628 double型浮点数隐含输出小数位数为6位,不过还可以通printf("%.20lf\n",a);来重新设置,.20中的20也可以换成其它的数值。 629 问题3:double型浮点数最大正数值和最小正数值分别是多少? 630 解答: 631 #include <iostream.h> 632 int main() 633 { 634 int y = 1030; 635 for (double x = 0.999999999; y-- ; x *= 2) cout << x << '\t'; 636 cout << endl; 637 y = 1030; 638 for (x = 0.000000001; y-- ; x /= 2) cout << x << '\t'; 639 cout << endl; 640 return 0; 641 } 642 由程序可得,double型浮点数最大正数值和最小正数值分别是1.79769e+308、1.73832e-319。 644 问题4:逻辑运算符号&&、||和!(它表示逻辑非)的相对优先级是怎样的?也就是说,a&&b||c应理解成(a&&b)||c还是a&&(b||c),或者随便怎么理解都可以? 645 解答:逻辑运算符号&&、||和!(它表示逻辑非)的相对优先级(由高到低)是!→ &&→||。a&&b||c应理解为(a&&b)||c。 646 问题5:if(a) if(b) x++; else y++;的确切含义是什么?这个else应和哪个if配套?有没有办法明确表达出配套方法,以避免初学者之困惑? 647 解答: 648 if(a) if(b) x++; else y++; 可以换成以下的形式: 649 if(a) 650 if(b) x++; 651 else y++; 652 else与if的配对的原则是else一般与最近没有配对的if进行配对。实际上,如果else与第一个if进行配对,可以采取加{}的方式进行: 653 if(a) 654 { if(b) x++;} 655 else y++; 656 如果else与第二个if进行配对,可以采取加{}的方式进行: 657 if(a){ 658 if(b) x++; 659 else y++; 660 } 661 以上这种加{}的方式,就不会引起歧义。662 1.6.5 小结 663 664 (1)本章介绍了常见的各种数据类型,以及他们所能表达的范围; 665 (2)本章介绍了scanf和printf语句的使用; 666 (3)本章介绍了逻辑判断的使用,理解并描述复杂的逻辑判断。 667 (4)程序设计是一门实践性很强学科,所以在学习时,给出两点建议:重视实验、学会模仿。668 1.6.6 上机练习 670 程序设计是一门实践性很强的学科,所以给出下面的练习。 671 习题1-1 平均数(average) 672 输入3个整数,输出它们的平均值,保留3位小数。 673 解答: 674 #include <stdio.h> 675 int main() { 676 int a,b,c; 677 double d; 678 scanf("%d%d%d",&a,&b,&c); 679 d=(double)(a+b+c); 680 printf("%.3lf\n",d/3.0); 681 return 0; 682 }683 习题1-2 温度(temperature) 684 输入华式温度f,输出对应的摄氏温度c,保留3位小数。提示:c=5(f-32)/9。 685 解答: 686 #include <stdio.h> 687 int main() { 688 int f; 689 double c; 690 scanf("%d",&f); 691 c=5*(f-32)/9; 692 printf("%.3lf\n",c); 693 return 0; 694 }695 习题1-3 连续和(sum) 696 输入正整数n,输出1+2+…+n的值。提示:目标是解决问题,而不是练习编程。 697 解答: 698 #include <stdio.h> 699 int main() { 700 int n; 701 scanf("%d",&n); 702 printf("%d\n",(1+n)*n/2); 703 return 0; 704 } 705 注意:本题利用了等差数列的公式,而不是像平常编程时,用循环来实现。706 习题1-4 余弦和正弦(sincos) 707 输入正整数(n<360),输出n度的正弦、余弦函数值。提示:使用数学函数。 708 解答: 709 #include <stdio.h> 710 #include <math.h> 711 int main() { 712 const double pi = 3.1415926; 713 int n; /* n是角度 */ 714 scanf("%d",&n); 715 printf("sin(n)=%lf\n",sin((n/180.0)*pi)); 716 printf("cos(n)=%lf\n",cos((n/180.0)*pi)); 717 return 0; 718 } 719 习题1-5 距离(distance) 720 输入4个浮点数x1,y1,x2,y2,输出平面坐标系中点(x1,y1)和点(x2,y2)的距离。 721 解答: 722 #include <stdio.h> 723 #include <math.h> 724 int main() { 725 double x1,x2,y1,y2; 726 scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); 727 printf("%lf\n",sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))); 728 return 0; 729 }730 习题1-6 偶数(odd) 731 输入一个整数,判断它是否为偶数。如果是,则输出“yes”,否则输出“no”。提示:可以用多种方法判断。 732 解答: 733 #include <stdio.h> 734 int main(){ 735 int n; 736 scanf("%d",&n); 737 if(n%2==0){ 738 printf("YES\n"); 739 } 740 else{ 741 printf("NO\n"); 742 } 743 return 0; 744 }745 习题1-7 打折(discount) 746 一件衣服95元,若消费满300元,可打八五折。输入购买衣服件数,输出需要支付的金额(单位:元),保留两位小数。 747 解答: 748 #include <stdio.h> 749 int main() { 750 int n; 751 double a; 752 scanf("%d",&n); 753 a=n*95.0; 754 if(a<300){ 755 printf("%.2lf\n",a); 756 } 757 else{ 758 printf("%.2lf\n",a*0.85); 759 } 760 return 0; 761 }762 习题1-8 绝对值(abs) 763 输入一个浮点数,输出它的绝对值,保留两位小数。 764 解答: 765 #include <stdio.h> 766 #include <math.h> 767 int main() { 768 double n; 769 scanf("%lf",&n); 770 printf("%.2lf",fabs(n)); 771 return 0; 772 }773 习题1-9 三角形(triangle) 774 输入三角形三边长度值(均无正整数),判断它是否能为直角三角形的三个边长。如果可以,则输出“yes”,如果不能,则输出“no”。如果根本无法构成三角形,则输出“not a triangle”。 775 解答: 776 #include <stdio.h> 777 int main() { 778 int a,b,c; 779 scanf("%d%d%d",&a,&b,&c); 780 if(a==b&&b==c) { 781 printf("no\n"); 782 } 783 if((a*a+b*b==c*c)||(a*a+c*c==b*b)||(b*b+c*c==a*a)) { 784 printf("yes\n"); 785 } 786 else { 787 printf("no\n"); 788 } 789 return 0; 790 }791 习题1-10 年份(year) 792 输入年份,判断是否为闰年。如果是,则输出“yes”,否则输出“no”。提示:简单地判断除以4的余数的是不够的。 793 解答: 794 #include <stdio.h> 795 int main() { 796 int n; 797 scanf("%d",&n); 798 if(n%4==0) { 799 if(n%100!=0) { 800 printf("no\n"); 801 } 802 else { 803 if(n%400==0) { 804 printf("yes\n"); 805 } 806 else{ 807 printf("no\n"); 808 } 809 } 810 } 811 else { 812 printf("no\n"); 813 } 814 return 0; 815 } 816 817 818 布 置 作 业 819 上机练习 习题1-7、1-8、1-9、1-10
转载于:https://www.cnblogs.com/lx17746071609/p/10311070.html
[算法竞赛入门]第一章_算法概述相关推荐
- 蓝桥杯比赛常考算法_备战蓝桥--算法竞赛入门第一章总结
笔者备战蓝桥杯先打算看完<算法竞赛入门经典>第2版,在这里写下第一章的笔记,供自己和大家参考. 鸡兔同笼问题 原题: 已知鸡和兔的总数量为n,总腿数为m.输入n和m,依次输出鸡的数目和兔的 ...
- [算法竞赛入门]第二章_循环结构程序设计
第2章 循环结构程序设计 [学习内容相关章节] 2.1for循环 2.2循环结构程序设计 2.3文件操作 2.4小结与习题 [学习目标] (1)掌握for循环的使用方法: (2)掌握while循环的使 ...
- 蓝桥杯算法竞赛系列第一章——位运算的奇巧淫技及其实战
遇见蓝桥遇见你,不负代码不负卿! 第二章"递归"已将更新咯,欢迎铁汁们点评!蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(上)_安然无虞的博客-CSDN博客 目录 一.位运算符 ...
- [算法竞赛]第四章_函数和递归
第4章 函数和递归 [教学内容相关章节] 4.1数学函数 4.2地址的指针 4.3递归 4.4本章小结 [教学目标] (1)掌握多参数.单返回值的数学函数的定义和使用方法: (2)学会用typedef ...
- 《算法导论3rd第一章》算法在计算中的作用
前言 什么是算法?为什么算法值得研究?算法的作用是什么 算法 算法就是任何良定义的计算过程,该过程取某个值或值的集合作为输入并产生某个值或值的集合做为输出.即就是把输入转换成输出的计算步骤的一个序列. ...
- 需求分析第一章_需求概述
1.需求是多层次的,包括业务需求.用户需求.功能需求和非功能需求. 2.需求的路线图: 涉众需要--系统的特性--建立软件的需求. 3.软件开发的目标,就是满足用户的需要. 4.从软件工程角度,需求分 ...
- 【Java开发语言 01】第一章 Java语言概述(基础常识+Java语言概述+Java程序运行机制及运行过程+Java语言环境的搭建+开发体验hello world+错误:编码GBK的不可映射字符)
java入门-第一章Java语言概述 1基础常识 软件开发 人机交互方式 常用的DOS命令(win系统,有一些直接通过命令执行的) 2 Java语言概述 什么是计算机语言 关于面向对象和面向过程 Ja ...
- 《算法竞赛入门经典——训练指南》第一章相关内容
#<算法竞赛入门经典--训练指南>第一章相关内容 希望各位大牛能指导! 红色为已经做了的...黄色背景是还有不懂地方,希望在年前能刷完第一章啊.... 更新版.google上貌似又加了ex ...
- 第一章 程序设计入门--算法竞赛入门经典
第一章 程序设计入门–算法竞赛入门经典 知识点一: int m=25; printf("%d\n",m); printf("%03d\n",m); 输出如下: ...
最新文章
- Activity管理(一):activity运行机制
- java 链表反转_LeetCode206 实现单链表的反转
- C#多线程 我的第一个多线程程序
- moxy json介绍_MOXy的@XmlVariableNode – JSON模式示例
- 嵌入式中常见的存储器总结(二)SRAM VS DRAM
- lvm快照备份mysql
- oracle估算数据增长,如何估算oracle 数据库,数据库对象历史增长情况
- Rational Rose 2003 逆向工程转换C++ / VC++ 6.0源代码成UML类图
- js 时间任意格式化 ,又来造轮子了
- CGLIB 和 JDK生成动态代理类的区别(转)
- Sqlalchemy 乱码解决方法
- 网平差中的基线定权(松弛因子)
- win7电脑网站服务器,Win7系统
- python编程和excel_Excel Vs. Python?为Excel正名
- viper12a电源电路图_VIPer12A功率开关电源IC.pdf
- 抖音昵称html,抖音个性网名带特殊符号 带漂亮符号的抖音昵称
- 起得比鸡早,睡得比狗晚,干得比驴多,吃得比猪差
- 单片机的俄罗斯方块游戏设计
- 致远OA漏洞学习——A6版本敏感信息泄漏漏洞
- java关联vss 80020009,80020009: Invalid password[src=SourceSafe,guid=null]
热门文章
- U盘检测-linux+QT
- Python音乐跳舞毯(基于海龟画图创作的作品,来自Python创意编程100例sprites篇_Python精灵模块)
- 金融风险管理基本框架
- 【小技巧】STA静态时序分析概述
- 2019第五届中国诗歌春晚致敬先贤
- 过程裁剪的理念和表现形式
- python爬虫豆瓣电影评价_Python 爬虫实战(1):分析豆瓣中最新电影的影评
- Hydration failed because the initial UI does not match what was rendered on the server.问题原因之一
- 详细分析Android中的引用机制Reference(WeakReference、SoftReference、PhantomReference)
- [0CTF 2016]piapiapia php反序列化字符串逃逸