算法竞赛入门 (一)语言篇 循环结构

掌握清单:

  • for while do..while 循环
  • 计数器和累加器
  • 用输出中间结果的方法调试
  • 用计时函数测试程序的效率
  • 用重定向方式读写文件
  • 用fopen方式读写文件
  • 用条件编译指示构建本地运行环境
  • 用编译选项 -Wall 获得更多的警告信息

一、for循环

程序1:for循环输出

#include<stdio.h>
int main(){
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++)
        printf("%d
",i);
    return 0;
}

细节:变量i定义在循环语句中,因此i在循环体外不可见

详细解释

# 建议议尽量缩短变量定义的范围 ------- 例如,在for循环中的初始化部分,定义循环变量

程序2:输出所有aabb形式的4位完全平方数

#include<stdio.h>
#include<math.h>
//aabb 是4位数字,所以a的范围——1-9,b的范围——0-9
int main(){
    for (int a = 1;a <= 9;a++){
        for(int b = 0;b <= 9;b++){
            //开始判定,aabb是不是平方数
            int n = a * 1100 + b * 11;
            //用一个变量m存储sqrt(n) 四舍五入后的整数,然后判定m*m是否等于n
            //floor(x)函数返回不超过x的最大整数
            int m = floor(sqrt(n) + 0.5);
            if(m*m == n)
                printf("%d
",n);
        }
    }
return 0; }

 结果:7744

floor(sqrt(n) + 0.5)  表示四舍五入

 # 浮点数的运算有可能存在误差,假设在经过大量运算后,由于误差的影响,整数1 变成了 0.99999999,floor的结果会是0 而不是 1,为了减小误差的影响,一般改为 四舍五入,即floor(x + 0.5)

 # 浮点运算可能存在误差,在进行浮点数 比较时,应该考虑到 浮点误差 

另一个思路是 枚举平方根 x,从而避免开平方操作

#include<stdio.h>
int main(){
    for(int x = 1; ; x++){
        int n = x *x;
        if (n < 1000)
            continue;
        if (n > 9999)
            break;
        int hi = n/100;
        int lo = n%100;
        if( hi/10 == hi%10 && lo/10 == lo%10){
            printf("%d
",n);
        }
    }
    return 0;
}

二、while  do...while 循环

程序3:  3n+1问题

 下列程序有Bug

#include<stdio.h>
int main(){
    int n,count = 0;
    scanf("%d",&n);
    while(n >1){
        if(n%2 ==1)
            n = n *3 +1;
        else
            n /= 2;
        count++;
    }
    printf("%d",count);
    return 0;
}

当输入较大的数时,如987654321,最后输出的是 1 

采用“”输出中间结果“的方法差错

#include<stdio.h>
int main(){
    int n,count = 0;
    scanf("%d",&n);
    while(n >1){
        if(n%2 ==1){
            n = n *3 +1;
            printf(" %d
",n);
        }else{
            n /= 2;
        }
        count++;
    }
    printf("%d",count);
    return 0;
}

 发现 ————>  乘法 溢出

 # c99并没有规定int类型 的确切大小,但在当前流行的竞赛平台上,int 都是32位 ———— -2147483648 ~ 2147483647

而本题中n的上限 10^9 只比int的上界稍微小点,极容易溢出

所以采用 long long即可 解决问题 —— 范围是: -2^63 ~ 2^63 - 1   输入时为 %lld

 后续详细讨论!

#include<stdio.h>
int main(){
    int n2,count = 0;
    scanf("%d",&n2);
    long long n = n2;
    while(n >1){
        if(n%2 ==1)
            n = n *3 +1;
        else
            n /= 2;
        count++;
    }
    printf("%d",count);
    return 0;
}

程序4:近似计算

#include<stdio.h>
int main(){
    double sum = 0;
    for(int i = 0;;i++){
        double term = 1.0 / (i*2 + 1);
        if(i%2 ==0)
            sum += term;
        else
            sum -= term;
        if(term < 1e-6)
            break;
    }
    printf("%.6f
",sum);
    return 0;
}
#include<stdio.h>
int main(){
    double sum = 0;
    int i = 0;
    double term = 0;
    do{
        term = 1.0/(i*2+1);
        if(i%2 == 0)
            sum += term;
        else
            sum -= term;
        i++;
    }while(term > 1e-6);
    printf("%.6f",sum);
    return 0;
}

三、循环的代价

程序5:阶乘之和

#include<stdio.h>
int main(){
    int n,s = 0;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        int factorial = 1; //在循环体开始处定义的变量,每次执行循环体的时候都会宠幸声明并初始化
        for(int j =1;j <=i;j++)
            factorial *= j;
        s += factorial;
    }
    printf("%d
",s % 1000000); //因为只要末6位,所以输出时需要对10^6 取模
    return 0;
}

 很显然,极容易溢出

 当 n = 10^6 时,更会溢出,但速度 极慢

 补充:要计算只包含加法、减法和乘法的整数表达式除以正整数n的余数,可以在每步计算之后对n取余,结果不变

下面,把程序改成“每步取模“的形式,然后加一个计时器,看看速度:

#include<stdio.h>
#include<time.h>
int main(){
    const int MOD = 1000000;
    int n,s = 0;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        int factorial = 1;
        for(int j = 1;j <=i;j++){
            factorial = (factorial * j %MOD);

        }
        s = (s + factorial) %MOD;
    }
    printf("%d
",s);
    printf("Time used = %.2f
",(double) clock() /CLOCKS_PER_SEC);
    return 0;
}

计时函数 clock() ———— 该函数返回程序目前为止运行的时间,在程序结束之前调用此函数,就可以获得整个程序的运行时间,再除以常数 CLOCKS_PER_SEC后,得到的值以秒s 为单位

 键盘的输入时间也被计算在内,为了避免输入数据的时间影响测试结果,可以使用一种称为“管道”的小技巧:

在windows命令行中执行 echo 20|abc ,系统就会自动把20输入,abc是程序名

四、算法竞赛中的输入、输出框架

程序6:数据统计

输入一些整数,求出他们的min max 和平均值(保留三位小数)。输入保证这些数都是 不超过1000的整数

 【有Bug】

#include<stdio.h>
int main(){
    int x,n =0,min,max,s = 0;
    while(scanf("%d",&x) == 1){
        s += x;
        if(x <min)
            min = x;
        if(x >max)
            max = x;
        n++;
    }
    printf("%d %d %.3f",min,max,(double) s / n);
    return 0;
}

 # scanf() 返回的是 成功输入的变量个数

 测试一下

 5002320 从哪里来的? ———— 变量在没有赋值之前的值是不确定的

 解决的办法就是 在使用之前赋初值

比较好的办法就是用文件 ———— 把输入数据保存在文件之中,输出数据也保存在文件之中。只要把事先输入数据保存在文件之中,就没必要每次重新输入了

使用文件最简单的方法就是 使用输入输出重定向

 只需要在main 函数入口处加上:

freopen("input.txt","r",stdin)
freopen("output.txt","w",stdout)

 上处语句使得scanf() 从 input.txt读入,printf() 写入 output.txt 

 算法竞赛中,选手应该严格遵守比赛的文件名规定,尤其是路径【不能加路径,哪怕是相对路径】

方法:在本机测试时使用文件重定向,但一旦提交到比赛就自动“”删除“”重定向语句

#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;    //使用INF的原因是:给定一个假想的无穷大
    while(scanf("%d",&x) == 1){
        s += x;
        if(x <min)
            min = x;
        if(x >max)
            max= x;
        /*
        printf("x = %d,min = %d,max = %d
",x,min,max);
        */
        n++;
    }
    printf("%d %d %.3f
",min,max,(double) s/ n);

    return 0;
}

 

 如果比赛 要求用文件输入输出,但禁止使用重定向的方式:

#include<stdio.h>
#define INF 1000000000
int main(){
    FILE *fin,*fout;
    fin = fopen("data.in","r");
    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 < min)
            min = x;
        if(x >max)
            max = x;
        n++;
    }
    fprintf(fout,"%d %d %.3f
",min,max,(double) s / n);
    fclose(fin);
    fclose(fout);
    return 0;
}

 

 

 

用fopen("con","r")的方法打开标准输入输出不是可移植的,在Linux下是无效的!!!!

 程序7:   多组数据问题

#include<stdio.h>
#define INF 1000000000
int main(){
    int x,n =0,min = INF,max = -INF,s = 0,kase = 0;
    while(scanf("%d",&n) == 1&& n){
        int s= 0;
        for(int i = 0;i <n;i++){
            scanf("%d",&x);
            s += x;
            if(x < min)
                min = x;
            if(x >max)
                max = x;

        }
        if(kase)
            printf("
");
        printf("Case %d: %d %d %.3f
",++kase,min,max,(double) s/n);

    }
    return 0;
}

 要点分析:

 

 

 

 

原文地址:https://www.cnblogs.com/expedition/p/11441404.html