- scanf的返回值由后面的参数决定
- 如果a和b都被成功读入,那么scanf的返回值就是2
- 如果只有a被成功读入,返回值为1
- 如果a和b都未被成功读入,返回值为0
- 如果遇到错误或遇到end of file,返回值为EOF
- 有经验的程序员总是尽量缩小变量定义的范围,当写了足够多的程序之后,这样做的优点会慢慢表现出来。
- 当嵌套的两个代码块中有同名变量时,内层的变量会屏蔽外层变量,有时会引起十分隐蔽的错误。
- warning:unused variable ‘s’ [-Wunused-variable]
-
表示为:
1e-6
- 要计算只包含加法、减法和乘法的整数表达式除以正整数n的余数,可以在每步计算之后对n取余,结果不变。
- clock(),该函数返回程序目前为止运行的时间。在程序结束之前调用此函数,便可获得整个程序的运行时间。这个时间除以常数CLOCKS_PER_SEC之后得到的值以“秒”为单位。键盘输入的时间也被计算在内。
- Windows和Linux均提供“管道”机制,用于把不同的程序串起来。例如,如果有一个程序aplusb从标准输入读取两个整数a和b,计算并输出a+b,还有一个程序sqr从标准输入读取一个整数a,计算并输出a 2 ,则可以这样计算(10+20) 2 : echo 10 20 | aplusb | sqr。
- 变量在未赋值之前的值是不确定的。特别地,它不一定等于0。
数据统计
输入一些整数,求出它们的最小值、最大值和平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。
分别用输入输出重定向和文件输入输出的方式实现。
输入输出重定向
只需在main函数的入口处加入以下两条。
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
有一种方法可以在本机测试时用文件重定向,但一旦提交到比赛,就自动“删除”重定向语句。
#define LOCAL
#include<stdio.h>
#define INF 1000000000
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif // LOCAL
int 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);
return 0;
}
- 重定向的部分被写在了#ifdef和#endif中。其含义是:只有定义了符号LOCAL,才编译两条freopen语句。
- 输出中间结果的printf语句写在了注释中——它在最后版本的程序中不应该出现,但是又舍不得删除它(万一发现了新的bug,需要再次用它输出中间信息)。将其注释的好处是:一旦需要时,把注释符去掉即可。
- 上面的代码在程序首部就定义了符号LOCAL,因此在本机测试时使用重定向方式读写文件。如果比赛要求读写标准输入输出,只需在提交之前删除#defineLOCAL即可。一个更好的方法是在编译选项而不是程序里定义这个LOCAL符号(不知道如何在编译选项里定义符号的读者请参考附录A),这样,提交之前不需要修改程序,进一步降低了出错的可能。
常见编译选项
#include<stdio.h>
int main()
{
int a, b;
scanf("%d%d", &a, &b);
int c = a+b;
printf("%d%d\n", c);
return 0;
}
执行gcc test.c -o test -Wall
test.c: In function 'main':
test.c:7:12: warning: format '%d' expects a matching 'int' argument [-Wformat=]
printf("%d%d\n", c);
^
- 另一个有用的选项是-DDEBUG,它在编译时定义符号DEBUG(可以换成其他,如-DLOCAL将定义符号LOCAL),这样,位于#ifdef DEBUG和#endif中间的语句会被编译。而在通常情况下,这些语句将被编译器忽略(注意,不仅是不会执行,连编译都没有进行)
- 文件重定向的那个程序改为
#include<stdio.h>
#define INF 1000000000
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif // LOCAL
int 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);
return 0;
}
cmd执行:gcc abc.cpp -o abc -DLOCAL
会生成abc.exe。执行abc.exe会生成data.out文件,记录程序的输出结果。
文件输入输出
#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 > max) {
max = x;
}
if(x < min) {
min = x;
}
n++;
}
fprintf(fout, "%d %d %.3f\n", min, max, (double)s/n);
fclose(fin);
fclose(fout);
return 0;
}
- 在算法竞赛中,如果不允许使用重定向方式读写数据,应使用fopen和fscanf/fprintf进行输入输出
- 重定向和fopen两种方法各有优劣。重定向的方法写起来简单、自然,但是不能同时读写文件和标准输入输出;fopen的写法稍显繁琐,但是灵活性比较大(例如,可以反复打开并读写文件)。
- 如果想把fopen版的程序改成读写标准输入输出,只需赋值“fin=stdin;fout=stdout;”即可,不要调用fopen和fclose(注释掉)。
一种常见的输入方式
- 输入包含多组数据,每组数据第一行是整数个数n,第二行是n个整数。n=0为输入结束标记,程序应当忽略这组数据。
while(scanf("%d", &n) == 1 && n)
习题2-1 水仙花数(daffodil)
输出100~999中的所有水仙花数。若3位数ABC满足ABC= + + ,则称其为水仙花数。例如153=1^3+5^3+3^3 ,所以153是水仙花数。
#include<stdio.h>
int main() {
int m, n;
while(scanf("%d%d",&m,&n)!=EOF) {
int count = 0;
for(int i = m; i <= n; i++) {
int a = i / 100;
int b = i / 10 % 10;
int c = i % 10;
if(a*a*a + b*b*b + c*c*c == i) {
if(count == 0) {
printf("%d", i);
} else {
printf(" %d", i);
}
count++;
}
}
if(count == 0) {
printf("no");
}
printf("\n");
}
return 0;
}
习题2-2 韩信点兵(hanxin)
相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。输入包含多组数据,每组数据包含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>
#include<math.h>
int main() {
int a,b,c;
int time = 1;
while(scanf("%d%d%d",&a,&b,&c)!=EOF) {
int ans = 0;
for(int i=10; i<=100; i++) {
if((i%3==a)&&(i%5==b)&&(i%7==c)) {
printf("Case %d:%d\n", time, i);
ans = 1;
break;
}
}
if(!ans) {
printf("Case %d:No answer\n", time);
}
time++;
}
return 0;
}
习题2-3 倒三角形(triangle)
输入正整数n≤20,输出一个n层的倒三角形。例如,n=5时输出如下:
#########
#######
#####
###
#
#include<stdio.h>
#include<math.h>
int main() {
int n;
while(scanf("%d",&n)!=EOF) {
for(int i=0; i<n; i++) {
for(int j=0; j<i; j++){
printf(" ");
}
for(int k=0; k<2*(n-i)-1; k++) {
printf("#");
}
printf("\n");
}
}
return 0;
}
习题2-4 子序列的和
输入两个正整数,n<m<106
,输出1/
+1/
+…+1/
,保留5位小数。输入包含多组数据,结束标记为n=m=0。提示:本题有陷阱。
样例输入:
2 4
65536 655360
0 0
样例输出:
Case 1: 0.42361
Case 2: 0.00001
#include<stdio.h>
#include<math.h>
int main() {
long long n,m;
int count = 0;
while(scanf("%lld%lld",&n,&m)!=EOF) {
int num = 1;
if(n==0 && m==0) {
break;
}
double sum = 0.0;
for(long long i=n;i<=m; i++) {
sum += 1.0/(double)(i*i);
}
printf("Case %d:%.5lf\n", ++count, sum);
}
return 0;
}
习题2-5 分数化小数(decimal)
输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。a,b≤10 6 ,c≤100。输入包含多组数据,结束标记为a=b=c=0。
样例输入:
1 6 4
0 0 0
样例输出:
Case 1: 0.1667
#include<stdio.h>
#include<math.h>
int main() {
int a,b,c;
while(scanf("%d%d%d",&a,&b,&c)!=EOF) {
if(a==0 && b==0 && c==0) {
break;
}
printf("%.*f\n",c,a/(b*1.0));//精确到小数点后c位
}
return 0;
}
习题2-6 排列(permutation)
用1,2,3,…,9组成3个三位数abc,def和ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3。按照“abc def ghi”的格式输出所有解,每行一个解。提示:不必太动脑筋。
#include<stdio.h>
int main() {
int abc, def, ghi;
int num[10];
for(int i=123; i<=329; i++) {
int count = 0;
for(int i=1; i<=9; i++) {
num[i] = 0;
}
abc = i;
def = 2*i;
ghi = 3*i;
num[abc/100]++;
num[abc/10%10]++;
num[abc%10]++;
num[def/100]++;
num[def/10%10]++;
num[def%10]++;
num[ghi/100]++;
num[ghi/10%10]++;
num[ghi%10]++;
for(int j = 1; j <= 9; j++) {
if(num[j] != 0) {
count++;
}
}
if(count == 9) {
printf("%d %d %d\n", abc, def, ghi);
}
}
return 0;
}