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

掌握清单:

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

要点分析:

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

时间: 2024-11-08 10:34:22

算法竞赛入门 (一)语言篇 循环结构的相关文章

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

第2章 循环结构程序设计 [学习内容相关章节] 2.1for循环 2.2循环结构程序设计 2.3文件操作 2.4小结与习题 [学习目标] (1)掌握for循环的使用方法: (2)掌握while循环的使用方法: (3)学会使用计算器和累加器: (4)学会用输出中间结果的方法调试: (5)学会用计时函数测试程序效率: (6)学会用重定向的方式读写文件: (7)学会fopen的方式读写文件: (8)了解算法竞赛对文件读写方式和命名的严格性: (9)记住变量在赋值之前的值是不确定的: (10)学会使用条

《算法竞赛入门经典(第二版)》pdf

下载地址:网盘下载 内容简介  · · · · · · <算法竞赛入门经典(第2版)>是一本算法竞赛的入门与提高教材,把C/C++语言.算法和解题有机地结合在一起,淡化理论,注重学习方法和实践技巧.全书内容分为12 章,包括程序设计入门.循环结构程序设计.数组和字符串.函数和递归.C++与STL入门.数据结构基础.暴力求解法.高效算法设计.动态规划初步.数学概念与方法.图论模型与算法.高级专题等内容,覆盖了算法竞赛入门和提高所需的主要知识点,并含有大量例题和习题.书中的代码规范.简洁.易懂,不

算法竞赛入门经典+挑战编程+USACO

下面给出的题目共计560道,去掉重复的也有近500题,作为ACMer Training Step1,用1年到1年半年时间完成.打牢基础,厚积薄发.   一.UVaOJ http://uva.onlinejudge.org  西班牙Valladolid大学的程序在线评测系统,是历史最悠久.最著名的OJ.   二.<算法竞赛入门经典> 刘汝佳  (UVaOJ  351道题)  以下部分内容摘自:http://sdkdacm.5d6d.com/thread-6-1-1.html   "AO

(Step1-500题)UVaOJ+算法竞赛入门经典+挑战编程+USACO

下面给出的题目共计560道,去掉重复的也有近500题,作为ACMer Training Step1,用1年到1年半年时间完成.打牢基础,厚积薄发. 一.UVaOJ http://uva.onlinejudge.org 西班牙Valladolid大学的程序在线评测系统,是历史最悠久.最著名的OJ. 二.<算法竞赛入门经典> 刘汝佳  (UVaOJ  351道题)  以下部分内容摘自:http://sdkdacm.5d6d.com/thread-6-1-1.html “AOAPC I”是刘汝佳(大

算法竞赛入门经典_4.3_递归

看代码 #include <stdio.h> int f(int n){ return n == 0?1:f(n-1)*n; } int main() { printf("%d\n", f(5)); return 0; } 上面f函数使用了递归,递归由两部分组成,一是递归头,二是递归体. 我们使用gcc调试工具 H:\编程书籍学习\算法竞赛入门经典2代码\算法入门经典第四章>b f 'b' 不是内部或外部命令,也不是可运行的程序 或批处理文件. H:\编程书籍学习\算

【算法竞赛入门经典】【第三章】课后习题(第二部分)

自从蓝桥杯之后,都没写博客了.今天将之前第三章还差的一部分习题答案补上. 3-4整数相加 这一题题目有提示,说选择合适的输入方式,即可简化问题.刚开始没想到cin,结果还用字符串来做,多亏别人提醒我一下,我才想起cin.惭愧啊.. #include <iostream> using namespace std; int main() { int a,b; char op; while(cin>>a>>op>>b){ switch(op){ case '+':

【算法竞赛入门】【第一章】课后习题

今天心血来潮,决定将<算法竞赛入门经典>里面的课后题,进行详解,先来第一发. 习题1-1 平均数(average) 对于第一题,相信即便是第一次接触编程的人.只要稍稍了解一下C的语法,也可轻易解决这一题.所以我也不多说了直接上代码. #include <stdio.h> int main() { int a, b, c; while(scanf("%d%d%d",&a,&b,&c)!=EOF){ printf("%.3lf\n&

《算法竞赛入门经典第二版》 P35 习题2-4 子序列的和(subsequence)

/* <算法竞赛入门经典第二版> P35 习题2-4: 输入两个正整数 n < m < 10^6,输出 (1/n)^2 + 1/(n+1)^2 +……+ 1/m^2,保留5位小数. 输入包含多组数据,结束标志为 m=n=0. 有错欢迎指出^_^ */ #include<stdio.h> int main() { int m,n,i,j=1; while(scanf("%d%d",&m,&n) != EOF) { double sum

《算法竞赛入门经典》动态规划复习

codevs 4979 数塔 1 #define N 100 2 #include<iostream> 3 using namespace std; 4 #include<cstdio> 5 int a[N][N],b[N][N],n; 6 int main() 7 { 8 scanf("%d",&n); 9 for(int i=1;i<=n;++i) 10 for(int j=1;j<=i;++j) 11 { 12 scanf("