算法竞赛入门经典学习笔记(二)——循环结构程序设计

  • scanf的返回值由后面的参数决定
    • 如果a和b都被成功读入,那么scanf的返回值就是2
    • 如果只有a被成功读入,返回值为1
    • 如果a和b都未被成功读入,返回值为0
    • 如果遇到错误或遇到end of file,返回值为EOF
  • 有经验的程序员总是尽量缩小变量定义的范围,当写了足够多的程序之后,这样做的优点会慢慢表现出来。
  • 当嵌套的两个代码块中有同名变量时,内层的变量会屏蔽外层变量,有时会引起十分隐蔽的错误。
    • warning:unused variable ‘s’ [-Wunused-variable]
  • 10 6 表示为: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= A 3 B 3 C 3 ,则称其为水仙花数。例如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/ n 2 +1/ ( n + 1 ) 2 +…+1/ m 2 ,保留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;
}

猜你喜欢

转载自blog.csdn.net/u011741311/article/details/80713399
今日推荐