C语言学习

  • 一:for循环
  • 二:while循环和do-while循环
  • 三:循环的代价
  • 四:算法竞赛中的输入输出框架
  • 五:习题
    • 5.1 水仙花数
    • 5.2 韩信点兵
    • 5.3 倒三角形
    • 5.4 子序列的和
    • 5.4 分数化小数
    • 5.5 排列
    • 5.6 思考题
  • 六:笔者注释
  • 七: 参考资料

本节中,主要讲解了for循环,for循环很多时候是作为遍历来使用的;构造一个aabb型数或者判断一个数是否为aabb型;判断一个数是否为完全平方数;continue和break语句的用法;while循环多数时候是在某个判断条件下多次循环,反正就是很微妙-_-。

一:for循环

1.1输出1,2,3…n的值

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

反思
1.在for中间使用分号,而在末尾千万不要使用分号。
2.定义变量尽量缩小其范围,例如此处将i定义在for循环中,而不是和n一起定义。
1.2输出形如aabb的4位完全平方数
思路一:开方再平方判断是否完全平方数

#include<stdio.h>
#include<math.h>
int main() {for (int i = 1; i <= 9; i++)for (int j = 0; j <= 9; j++)//i只能从1开始,而j课从0开始{int n = i * 1100 + j * 11;//构造aabb型int m = floor(sqrt(n) + 0.5);//判断是否完全平方/*floor表示不超过的整数加上0.5表示四舍五入,防止浮点运算出现误差相当于floor(x+0.5)=1的区间为0.5<=x<1.5*/if (n == m * m)printf("%d\n", n);}return 0;
}

自写出错点: 判断是否完全平方和判断输出要写到循环体内,不要和循环体平级,否则这两个语句没有起到作用。

思路二:在平方数中找aabb型

#include<stdio.h>
int main() {for (int i = 1;; i++)//没有加入判断语句{int n = i* i;if (n < 1000) continue;//continue跳回for循环的开始,执行下一次循环if (n > 9999)break;//终止循环int hi = n / 100;int low = n % 100;if (hi / 10 == hi % 10 && low / 10 == low % 10)printf("%d\n", n);}return 0;
}

自写出错点:
1.continue和break的作用:continue帮助节约时间,避免在<1000中的数执行后面的代码;而break作为终止循环的条件。
2.在判断是否为aabb型数字时,可以将前面两个和后面两个分开来重新赋值,便于编写和理解。复杂→简单始终有用!

二:while循环和do-while循环

2.1 3n+1问题
对于任意大于1的自然数n,若n为奇数,则将n变成3n+1,否则变成n的一半。经过若干次这样的变换,一定会使n变为1。输入n,求出变换次数。n<=109
样例输入:
3
样例输出“
7

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

注意
1.在count定义的时候,就对count进行初始化,否则后面count无法进行count++。
2.当输入的数字为987654321时,在允许的输入内,但是结果是1,明显错误。
在查找错误的时候我们通常采取输出中间变量来测试。如在count++后面加上printf(“%n\n”,n),可以看到会输出一个负数。
3.本题中乘法溢出。由于32位的int型整数的范围为-231~ 231-1,3*n+1溢出。
4.long long的取值范围为-263~ 263-1,在C99中需要将%d改成%lld,而在MinGW的gcc中改成%I64d。

自写出错
在输入的时候一定要加上&。count一定要初始化,否则++是无意义的。

//for循环
#include<stdio.h>
int main() {int n, count=0;scanf("%d", &n);for (; n > 1; ) {if (n % 2 == 1) {n = 3 * n + 1;}else n = n / 2;count ++;}printf("%d\n", count);return 0;
}
#include<stdio.h>
int main() {int n2, count = 0;scanf("%d", &n2);long long n = n2;//采用在中间使用long long n,避免了输入输出时的周折while (n > 1) {if (n % 2 == 1)n = 3 * n + 1;else n = n / 2;count++;}printf("%d\n", count);return 0;
}

感悟
避免溢出的方法一个方法是使用long long,但是long long在输入输出是可能出错,所以一个解决办法是输入输出时使用int型,输入输出后再将int的值转给long long型。

2.2 近似计算
计算Π/4=1-1/3+1/5-1/7+…直到最后一项小于10-6

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

自写反思
在条件要在循环体中才能开始时,一般使用for语句,不写for中的条件,在语句中使用break语句进行控制。

三:循环的代价

例题:输入n,计算S=1!+2!+3!+…+n!的末六位(不含前导0).n<=106,n!表示前n个正整数之积。
样例输入
10
样例输出:
37913

#include<stdio.h>
#include<time.h>
int main() {int n, s = 0;const int MOD = 1000000;//定义模的常量,增强程序的可读性。scanf("%d", &n);for (int i = 1; i <= n; i++) {int factorial = 1;//每次循环,都要对factorial重新声明并且进行初始化for (int j = 1; j <= i; j++) {factorial =factorial*j%MOD;//factorial作为累乘器,意思为阶乘}s = (s + factorial) % MOD;//只包含加法、减法、乘法的整数表达式除以正整数n的余数,可以在每步计算之后对n取余,结果不变。}printf("%d\n", s );printf("%.3f\n", (double)clock() / CLOCKS_PER_SEC);//引入时间函数,注意每次都要除以CLOCKS_PRE_SEC。return 0;
}

思考:
1.这段代码在n比较大的时候,时间会比较大,程序效率低下。
2.阶乘25!的末尾有6个0,所以从第25项开始,所有项都不会影响和的后六位数字。所以在最开始加上if(n>25)n=25,效率和溢出都会改变很大。
3.文中提到的每次都取余,因为前面的始终只能影响更高位的变化,我们只关注后面的六位,所以把前面的去掉不影响后面的变化。

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

例题: 数据统计
输入一些整数,求出它们的最小值、最大值、平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。
样例输入:
2 8 3 5 1 7 3 6
样例输出:
1 8 4.375

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

反思:
1.通过scanf("%d", &x)返回的是成功输入的变量的个数。当输入结束时,scanf无法再次读取x,返回0.
2.Windows系统下,输入结束后:按Enter,再按Ctrl+Z,最后按Enter结束输入。 Linux系统下,按Ctrl+D结束输入。
3.浮点计算在前面加上(double)即可。
4.max和min要制定初值,否则机器会开始随机生成数,结果报错。本处使用常量来,min初值要大,max初值要小。但问题:INF数据不够大或者运算溢出。

进阶:
暴露问题:输入要逐行输入,输出时一卷屏前面的就看不见,且与标准答案比较不方便。
解决办法:通过文件。
实际操作:输入输出重定向,在main函数的入口加上

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

注意事项:有的比赛不能使用文件,或者不能使用重定向方式读取文件,看请比赛要求是否是标准输入输出。并且输入输出文件名要仔细,不要使用相对路径或者绝对路径。
自我测试完成后删除重定向语句:

#define LOCAL
#include<stdio.h>
#define INF 1000000
int main() {#ifdef LOCALfreopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout);
#endifint x, n = 0, max = -INF, min = INF, s = 0;while (scanf("%d", &x) == 1) {s += x;if (max < x)max = x;if (min > x)min = x;/*printf("%d %d %.3f\n", min, max, (double)s / n);*/n++;}printf("%d %d %.3f\n", min, max, (double)s / n);return 0;
}

注意:
1.重定向部分写在#ifdef和#endif中。表示只有定义了符号LOCAL,才编译中间的两条。上程序首部就定义了LOCAL,提交时删除即可。或者在编译选项中定义LOCAL。
2.printf在/* */中可以用来中间检测输出。
非重定向版:

#include<stdio.h>
#define INF 1000000
int main() {FILE* fin, * fout;
fin=fopen("date.in", "rb");
fout=fopen("date.out", "wb");
int x, n = 0, max = -INF, min = INF, s = 0;
while (fscanf(fin,"%d",&x) == 1)
{s += x;
if (max < x)max = x;
if (min > x)min = x;
n++;
}
fprintf(fout,"%d %d %.3f\n", min, max, (double)s / n);
fclose(fin);
fclose(fout);
/*声明变量fin和fout,把scanf换成fscanf
第一参数为fin;printf换成fprintf,第一参数为fout
最后执行fclose,关闭两个文件*/return 0;
}

思考:
1.重定向文件更简洁,但不能同时文件读写和标准输入输出。
fopen写法繁琐,可以重复打开读写文件。将上文件改为标准输入/出,只需要赋值:fin=stdin;fout=stdout;同时不调用fopen和fclose

例题: 数据统计
输入一些整数,求出它们的最小值、最大值、平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。
输入包含多组数据,每组数据的第一行是整数个数n,第二行是n个整数。n=0为输入结束标志,程序忽略此数组。相邻两组数据之间应输出一个空行。
样例输入:
8
2 8 3 5 1 7 3 6
4
-4 6 10 0
0
样例输出:
Case 1:1 8 4.375
Case 2:-4 10 3.000

#include<stdio.h>
#define INF 1000000
int main() {int n=0, s = 0,x,kase=0;while (scanf("%d", &n) == 1 && n) {int s = 0 ,max = -INF, min = INF ;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;
}

反思:
1.输入循环:在n=0作为输入标记并仍然判断scanf的返回值,是增加程序的鲁棒性。
2.kase的使用:kase为数据编号计数器。当输入第二行数据时,会空一行。
3.min和max放置于循环内:在进行下一组运算时能够重置,否则将会影响下一组的结果。

五:习题

5.1 水仙花数

题目:
输出100~999中的所有水仙花数。若三位数ABC满足ABC=A3+B3+C3,则称其为水仙花数,例如153=13+53+33

#include<stdio.h>
int main() {for (int i = 100; i <= 999; i++) {int k, l, m;k = i / 100;l = i / 10 % 10;m = i % 10;if (i == k * k * k + l * l * l + m * m * m)printf ("%d\n",i);}return 0;
}

思考:
本题中分出每位的数字很重要,%和/的运算要清楚。

5.2 韩信点兵

题目:相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。输入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>
int main() {int a, b, c,kase=0;//计数变量scanf("%d%d%d", &a, &b, &c);for (int i = 2; i <= 19; i++) {int n = 5 * i + b;if ((n - a) % 3 == 0 && (n - c) % 7 == 0) {printf("Case%d :%d\n",++kase, n);}}if (kase == 0)printf("No Answer");return 0;
}

反思:
第一次卡壳:未能找到3、5、7的表示。
第二次卡壳:no answer这个条件语句不知道怎么放置,且没有使用计数变量进行输出。

5.3 倒三角形

输入正整数n<=20,输出一个n层的倒三角形。例如,n=5时输出如下:

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

反思:
本题需要深入理解遍历原理和顺序执行规则。

5.4 子序列的和

输入两个正整数n<m<106 ,输出1/n2+ 1/(n+1)2+…+1/m2,保留5位小数。输入包含多组数据,结束标记为n=m=0。提示:本题有陷阱。
样例输入:
2 4
65536 655360
0 0

样例输出:
Case 1: 0.42361
Case 2: 0.00001

5.4 分数化小数

输入正整数a, b, c,输出a/b的小数形式,精确到小数点后c位。a, b <= 106,c <= 100。输入包含多组数据,结束标记为a=b=c=0.

样例输入:
1 6 4
0 0 0

样例输出:

Case 1:0.1667

5.5 排列

用1,2,3…9组成3个三位数abc, def, ghi, 每个数字恰好使用一次,要求:abc:def:ghi=1:2:3,按照“abc def ghi”的格式输出所有解,每行一个解。

5.6 思考题

六:笔者注释

本文为学习《算法竞赛入门经典(第2版)》所做的学习笔记,仅作学习交流使用,切勿用作他途!如有侵权,联系立删。本文内容可能会有理解错误或遗漏,望乞指正,不胜感激!

七: 参考资料

刘汝佳. 算法竞赛入门经典[M]. 第2版. 北京 : 清华大学出版社, 2014

算法竞赛入门(2)学习笔记——循环结构程序设计相关推荐

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

    第2章 循环结构程序设计 [学习内容相关章节] 2.1for循环 2.2循环结构程序设计 2.3文件操作 2.4小结与习题 [学习目标] (1)掌握for循环的使用方法: (2)掌握while循环的使 ...

  2. 20130328java基础学习笔记-循环结构for以及for,while循环区别

    1.循环结构:for讲解 class ForDemo {     public static void main(String[] args)     {         /*         for ...

  3. 《机器学习算法竞赛实战》学习笔记4.特征工程

    吴恩达老师有言:"机器学习在本质上还是特征工程,数据和特征决定了机器学习的上限,模型和算法只是逼近这个上限而已." 特征工程主要分为:数据预处理.特征变换.特征提取.特征选择四部分 ...

  4. 《机器学习算法竞赛实践》学习笔记(3)竞赛概述问题建模

    竞赛概述 竞赛平台 Kaggle.天池.DataFountain.DataCasstle.Kesci(和鲸).JDATA.FlyAI.AI Challenger 公众号 Coggle数据科学.Kagg ...

  5. 算法学习之循环结构程序设计

    for循环 打印1,2,3,...,n每个占一行. #include <conio.h> #include<stdio.h> int main(){int i,n;scanf( ...

  6. 《算法竞赛进阶》学习笔记

    N数码判定性问题 大意就是给你两个N数码(N为奇数),判定是否能互相转化的问题 首先展开成一维,空格不计 如 1 2 3 4 5 6 7 0 8转化为1 2 3 4 5 6 7 8 然后判断两个序列的 ...

  7. 算法竞赛入门经典读书笔记(三)7.2枚举排列

    7.2.1生成1~n的所有排列 递归调用的方法可以实现. [cpp] view plaincopy #include <iostream> using namespace std; voi ...

  8. 算法竞赛入门经典读书笔记(四)7.3子集生成

    输入一个数n,输出集合0,1,2,3,n-1的全部子集 方法一:增量构造法: #include <iostream> using namespace std; void print_sub ...

  9. 算法竞赛入门经典读书笔记(二)7.1简单枚举

    7.1.1简单枚举 除法 输入正整数n,按从小到大的顺序输出所有形如abcde/fghij=n的表达式,其中a~j恰好为数字0~9的一个排列,2<=n<=79. 样例输入: 62 样例输出 ...

最新文章

  1. 嵌入式学习笔记之XMODEM
  2. Xamarin XAML语言教程ContentView视图作为自定义视图的父类
  3. 闪回恢复区 (Flash Recovery Area)
  4. web笔记Error:That IP address
  5. LeetCode算法题-Repeated String Match(Java实现)
  6. HttpResponse 类
  7. 动态打字效果 html,html5 svg酷炫的打字动画特效
  8. 力扣572. 另一棵树的子树(JavaScript)
  9. AWS学习笔记(四)--CLI创建EC2时执行脚本
  10. vue安装vue-pdf(预览pdf)(2021/03/02)
  11. Java毕业设计-疫情防控系统
  12. TI PFC+LLC解决方案在工业电源中的应用-电子研习社
  13. 头牌知产介绍燃气灶商标注册类别属哪一类?
  14. 怎么用显卡计算_教师:课程表的时间怎么用公式自动计算?
  15. Unity3d中UGUI组件精简复盘(十九)ContentSizeFitter组件
  16. (C语言)signed和unsigned类型转化
  17. shineblink 紫外线测量
  18. 苹果电脑python怎么安装request_python怎么装request
  19. STM32F05x加入RDP(LV1)后,Segger无法Unlock的解决办法
  20. Office 365身份认证--深度解析(二)

热门文章

  1. 音频特效滤镜 via Media Foundation Transform (MFT)
  2. 地磁基本知识(三)磁异常处理与转换
  3. 【论文分享】基于微信小程序的快递取寄系统设计与实现
  4. 通达信l2行情接口有自动下单功能吗?
  5. 双非生源30%,计科评级“A-”,中南计算机性价比超高
  6. 供独立游戏开发者参考的2D美工教程(四)
  7. 遇到的Exception/error及解决办法记录汇总
  8. 关于Java中UML图的说明
  9. RichEdit读取rtf格式
  10. ATA注会考试系统配置