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

Posted expedition

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法竞赛入门 语言篇 循环结构相关的知识,希望对你有一定的参考价值。

掌握清单:

  • 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\\n",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",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",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",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\\n",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\\n",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\\n",s);
    printf("Time used = %.2f\\n",(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\\n",x,min,max);
        */
        n++;
    
    printf("%d %d %.3f\\n",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\\n",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("\\n");
        printf("Case %d: %d %d %.3f\\n",++kase,min,max,(double) s/n);

    
    return 0;

 

 要点分析:

技术图片

 

 技术图片

 

 技术图片

 

 技术图片

 

 技术图片

以上是关于算法竞赛入门 语言篇 循环结构的主要内容,如果未能解决你的问题,请参考以下文章

[算法竞赛入门]第二章_循环结构程序设计

《算法竞赛入门经典(第2版)》pdf下载在线阅读,求百度网盘云资源

算法竞赛从入门到进阶

《算法竞赛入门经典》5.12TeX括号

算法竞赛入门经典 第二版 1-3答案

算法竞赛入门经典_6数据结构基础