2.4.1 输出技巧
#include <stdio.h> int main(void) { int i, n; scanf("%d", &n); for (i = 1; i <= n; i++) printf("%d\n", i); return 0; }目的:输出2, 4, 6, 8, ..., 2n
printf("%d\n", 2*i);\
任务2:只修改for循环体
for (i = 2; i <= 2*n; i += 2)
2.4.2 浮点数陷阱
#include <stdio.h> int main(void) { double i; for (i = 0; i != 10.0; i += 0.1) printf("%.1f\n", i); return 0; }
#include <stdio.h> int main(void) { double i; int j; for (i = 0, j = 0; i != 10.0 && j != 102; i += 0.1, j++) { printf("%.50f\n", i); } return 0; }
这里还发现如果把10.0改为0.5是可以正确运行的,代码如下:
#include <stdio.h> int main(void) { double i; for (i = 0; i != 0.5; i += 0.1) { printf("%.50f\n", i); } return 0; }
总之不要用浮点数来充当计数器,因为浮点数的表示是可以允许有误差的。
2.4.2 64位整数
像杭电OJ可能是Windows平台上建立的,测试是要用"%I64d"才能编译通过,而有些OJ就用"%lld"(long long int)
2.4.4 C++的输入输出
略
【上机练习】
习题2-1 位数(digit)
输入一个不超过10^9的正整数,输出它的位数。例如12735的位数是5。请不要使用任何数学函数,只能用四则运算和循环语句实现。
#include <stdio.h> int main(void) { int num;//garantee less than 10^9 int i, digit = 0; scanf("%d", &num); for (i = 1; num/i != 0; i*=10) digit++; printf("%d\n", digit); return 0; }
习题2-2 水仙花数(daffodil)
输出100~999中的所有水仙花数。若3位数ABC满足ABC=A^3+B^3+C^3,则称其为水仙花数。例如153=1^3+5^3+3^3,所以153是水仙花数。
#include <stdio.h> int main(void) { int i; int a, b, c; for (i = 100; i != 1000; i++) { a = i/100; b = i/10%10; c = i%10; if (a*a*a + b*b*b + c*c*c == i) printf("%d\n", i); } return 0; }
相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。输入3个非负数a, b, c,表示每种队形排尾的人数(a<3, b<5, c<7),输出总人数的最小值(或报告无解)。已知总人数不小于10,不超过100。
样例输入:2 1 6
样例输出:41
样例输出:2 1 3
样例输出:No answer
#include <stdio.h> int main(void) { int a, b, c; int i; scanf("%d%d%d", &a, &b, &c); for (i = 10; i != 100; i++) { if (i%3 == a && i%5 == b && i%7 == c) { printf("%d\n", i); break; } } if (i == 100) printf("No answer\n"); return 0; }枚举量还不到100,而且发现满足条件后直接退出循环,i也不会增加,确保输出0 4 0,输出是99,而不会输出99然后又输出100;i == 100成立等价于没有执行过break;语句,所以就是无解。
习题2-4 倒三角形(triangle)
输入正整数n<=20,输出一个n层的倒三角形。例如n = 5时输出如下:
(9个#一行,7个一行,5个一行,3个一行,1个一行,且除第一行外,每行最中间的#与上一行最中间的#对齐)
#include <stdio.h> int main(void) { int n, m; int i, j; scanf("%d", &n); m = 2*n-1; for (i = 0; i != n; i++) { for (j = 0; j != m; j++) printf((j<i || j > m-1-i) ? " " : "#"); printf("\n"); } return 0; }
设计思路:首先在for循环里写一行代码printf("#");即可以输出一个n*m的矩阵;然后再做调整!我们观察如果要构成倒三角,每一行中空格取代#处的数目与行号序号成正相关,不难写出在位置(i, j)满足(j < i || j > m-1-j)时用空格来代替#。如果认为三角形右边的空格是多余的,也可以分三路,符合条件j>m-1-i的位置什么也不做,即执行空语句。
习题2-5 统计(stat)
输入一个正整数n,然后读取n个正整数a1, a2, ..., an,最后再读一个正整数m。统计a1, a2, ..., an中有多少整数的值小于m。提示:如果重定向和fopen都可以使用,哪个比较方便?
暂略。。。
习题2-6 调和级数(harmony)
输入正整数n,输出H(n) = 1+(1/2)+(1/3)+...+(1/n)的值,保留3位小数。例如n=3时答案为1.833。
#include <stdio.h> int main(void) { int n; int i; double sum = 0; scanf("%d", &n); for (i = 1; i != n+1; i++) { sum += 1.0/i; } printf("%.3f\n", sum); return 0; }
习题2-7 近似计算(approximation)
计算(PI/4) = 1 - 1/3 + 1/5 - 1/7 +...,直到最后一项小于10^(-6)。
理解题意:意思就是计算右边的式子,计算到满足下述条件时停止。条件是:最后一项(不带符号)小于0.000001
然后我设置输出右边式子结果的4倍,作为PI的近似值
#include <stdio.h> #define N 0.000001 int main(void) { int sign = 1, i = 2; double cur = 1.0, sum = 0.0; for (; ;) { if (cur < N) break; sum += cur*sign; cur = 1.0/(i*2-1); i++; sign = -sign; } printf("PI ~= %.6f\n", 4*sum); return 0; }结果输出是:PI ~= 3.141591
习题2-8 子序列的和(subsequence)
输入两个正整数n<m<10^6,输出 1/n^2 + 1/(n+1)^2 + ... + 1/m^2,保留5位小数。例如n=2, m=4时答案是0.42361; n = 65536, m = 655360时答案是0.00001。注意:本题有陷阱。
#include <stdio.h> int main(void) { double n, m; double i, sum = 0.0; scanf("%lf%lf", &n, &m); for (i = n; i <= m; i++) { sum += 1.0/(i*i); } printf("%.5f\n", sum); return 0; }首先分母是正整数求平方,可能会溢出,所以要使用浮点数。
习题2-9 分数化小数(decimal)
输入正整数a, b, c,输出a/b的小数形式,精确到小数点后c位。a, b <= 10^6,c <= 100。例如a=1, b=6, c=4时应输出0.1667。
刚开始想到一个用数组(字符串)实现的方法,如下
#include <stdio.h> #define N 110 char str[N]; int main(void) { double a, b; int c; char *p; scanf("%lf%lf%d", &a, &b, &c); sprintf(str, "%.100f", a/b); for (p = str; *(p-1) != '.'; p++) printf("%c", *p); for (; *p != '\0'; p++) { if (c-- == 1) break; printf("%c", *p); } printf("%c\n", *(p+1)-'0' >= 5 ? *p+1 : *p); return 0; }
说明:首先把a/b精确到100位的结果作为字符串存入数组str中,然后输出小数点之前的部分,接着输出c-1位小数,再根据c+1位小数是否大于或等于5输出第c位小数。
补充:其实这个程序是错的!它有两个严重的BUG.
第一个是*(p-1)第一次执行时,数组第一个元素前面一个位置的地址是未定义的(UB),这种代码就是错的!
第二个是当遇到4.425987时候,如果我保留4位呢?结果应当是4.4260。但是上述程序只是简单地仅对最后一位小数作修改或不修改,输出的结果会是4.425:
最后发现,格式化输出还有特殊用法,比如
printf("%*.*lf\n", 8, 4, (double)10/3);
所以这个题目的标准答案应该是:
#include <stdio.h> int main(void) { int a, b, c; scanf("%d%d%d", &a, &b, &c); printf("%.*f\n", c, (double)a/b); return 0; }
习题2-10 排列(permutation)
用1,2,3,...,9组成3个三位数abc, def和ghi,每个数字恰好使用一次,要求abc:def:ghi = 1:2:3。输出所有解。提示:不必太动脑筋。
#include <stdio.h> int main(void) { int i, j, k; int i1, i2, i3; int j1, j2, j3; int k1, k2, k3; int c, count; for (i = 123; i <= 329; i++) { j = i*2; k = i*3; i1 = i/100, i2 = i/10%10; i3 = i%10; j1 = j/100, j2 = j/10%10; j3 = j%10; k1 = k/100, k2 = k/10%10; k3 = k%10; for (c = 1; c != 10; c++) { count = 0; if (c == i1) count++; if (c == i2) count++; if (c == i3) count++; if (c == j1) count++; if (c == j2) count++; if (c == j3) count++; if (c == k1) count++; if (c == k2) count++; if (c == k3) count++; if (count > 1) break; } if (c == 10 && i2 != 0 && i3 != 0 && j2 != 0 && j3 != 0 && k2 != 0 && k3 != 0) printf("%d %d %d\n", i, j, k); } return 0; }思路:暴力枚举,不过因为不能用数组,所以写起来比较繁琐,目前只能想到这种方法了。
程序输出了四组:
192 384 576
219 438 657
273 546 819
327 654 981