zoukankan      html  css  js  c++  java
  • 算法竞赛入门 (一)语言篇 循环结构

    掌握清单:

    • 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;
    }

     要点分析:

     

     

     

     

  • 相关阅读:
    黄聪:VirtualBox 安装ghost版windows XP
    黄聪:Delphi 关键字详解[整理于 "橙子" 的帖子]
    黄聪:全局变量 HInstance 到底是在什么时候赋值的?
    黄聪:演示 Rect、Bounds 生成 TRect 的区别
    黄聪:C#操作合并多个Word文档
    黄聪:C# .Net三层架构[转]
    黄聪:遗传算法实现自动组卷、随机抽题
    黄聪:SQL转换日期字段的问题——SQL中CONVERT转化函数的用法[转]
    黄聪:System 提供的编译期函数
    黄聪:语言字符集
  • 原文地址:https://www.cnblogs.com/expedition/p/11441404.html
Copyright © 2011-2022 走看看