重复 n 次
如何实现执行某种操作若干次呢?可以设定一个整数 i,结合判断条件实现
给 i 赋一个初值
设定循环结束时 i 的终值或终止条件
每次循环后,i 的值如何变化
本质上就是产生一个序列,序列个数就是循环次数
(1)重复 10000 次输出 a
第 1 部分:设定 i = 1;(只执行 1 次)
第 2 部分:当 i <= 10000; 时,继续执行循环;当 i > 10000 时,退出循环;
第 3 部分:每次循环后,++i;
C 语言中的 for 循环语法:
for (第 1 部分; 第 2 部分; 第 3 部分) { 代码 }
注意:小括号中有且只有 2 个分号!!!
#include<stdio.h>//连续输出 10000 个 aintmain(){
for (int i = 1; i <= 10000; ++i)
printf("a");
return0;
}
#include<iostream>intmain(){
for (int i = 1; i <= 10000; ++i)
std::cout << 'a';
return0;
}
(2)重复 n 次输出 a,并要求每个 a 前面带上序号,逐行输出
#include<iostream>//连续输出 n 个 aintmain(){
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
printf("%d: a\n", i);
return0;
}
#include<iostream>usingnamespacestd;
intmain(){
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
cout << i << ": " << "a\n";
return0;
}
做数列的计算
(1)从 1 加到 10000 的和
第 1 部分:设定 i = 1;(只执行 1 次)
第 2 部分:当 i <= 10000 时,继续执行循环;
第 3 部分:++i;
//需要将 1 到 10000 的数字依次与 sum 相加#include<iostream>//连续输出 10000 个 aintmain(){
int sum = 0;
for (int i = 1; i <= 10000; ++i)
sum += i;
printf("%d", sum);
return0;
}
while 循环
for循环中的第1部分
while (for循环中的第2部分) {
...
for循环的第3部分
}
还是以求 1 到 10000 的数字之和为例:
#include<stdio.h>intmain(){
int sum = 0, i = 1;
while (i <= 10000) {
sum += i;
++i;
}
printf("%d", sum);
return0;
}
do-while 循环
for循环第1部分
do {
...
for 循环第3部分
} while (for循环的第2部分);
从代码可以看出,它第一次执行循环体语句前,并没有执行第2部分的判断,而是在第一次循环以后,从第二次循环起,才每次先判断再执行。
for 和 while 循环最少可能执行 0 次,而 do-while 循环至少要执行 1 次。
#include<stdio.h>intmain(){
int sum = 0, i = 1;
do {
sum += i;
++i;
} while (i <= 10000);
printf("%d", sum);
return0;
}
break 语句
任何时候,在函数体内都可以使用 break; 直接退出循环(注意只能退出一层)。
continue 语句
使用 continue; 语句可以直接跳过当前循环体内位于 continue; 后面的所有语句,直接执行for循环的第3部分,进入下一次循环。
#include<stdio.h>//输入n个数,求其中奇数的和intmain(){
int n, i = 1, x, sum = 0;
scanf("%d", &n);
for (i = 1; i <= n; ++i) {
scanf("%d", &x);
//若x为偶数,则跳过后面的语句,直接去执行++i;输入下一个xif (x%2 == 0) continue;
sum += x;
}
printf("%d", sum);
return0;
}
处理数列的前后关系
需要处理一个数列的每个数与其前后的数之间的关系时,通常我们需要用到一个辅助的临时变量,我们起名叫 last。
这时我们可能会用一个特殊的输入方式:将第一个数和 n 在同一个 scanf 中先输入,并将其赋值给 last。
break:跟在 switch 语句中遇到的一样,此时用 break 可以 直接退出循环。
循环外如何判断是 break 退出:i <= n;//与 for 循环中的条件一致
#include<iostream>intmain(){
int i, n, x, last;
scanf("%d%d", &n, &last);
for (i = 2; i <= n; ++i) {
scanf("%d", &x);
//如果发现非递增,就退出循环,此时 i <= nif (x <= last) break;
last = x;
}
if (i <= n) puts("NO");
elseputs("YES");
return0;
}
三目运算符
#include<iostream>intmain(){
int i, n, x, last;
scanf("%d%d", &n, &last);
for (i = 2; i <= n; ++i) {
scanf("%d", &x);
//如果发现非递增,就退出循环,此时 i <= nif (x <= last) break;
last = x;
}
i <= n ? puts("NO") : puts("YES");
return0;
}
#include<iostream>intmain(){
int i, n, last = -1, x, cnt;
scanf("%d", &n);
for (i = 1; i <= n; ++i) {
scanf("%d", &x);
if (x%30 == 0) {
if (x>last && last%30==0) ++cnt;
else cnt = 1;
}
if (cnt == 5) break;
last = x;
}
if (cnt == 5) printf("Yes\n%d", i-4);
elseputs("No");
return0;
}
上面代码的 for 循环中,其实 x%30 如果不为 0,后面的嵌套显得有些累赘,能否让代码更简洁呢?可以,需要引入循环的另一个关键字——continue
continue——停止当前这次循环,直接去执行 ++i, 跳入下一次循环
#include<iostream>intmain(){
int n, i, x, last = -1, cnt = 0;
scanf("%d", &n);
for (i = 1; i <= n; ++i, last = x) {
scanf("%d", &x);
if (x % 30) { cnt = 0; continue; }
cnt && x>last ? ++cnt : (cnt=1);
if (cnt == 5) break;
}
if (cnt < 5) puts("No");
elseprintf("Yes\n%d", i-4);
return0;
}
循环输入问题
我们经常会遇到一些题目的输入是不给定输入数据个数的,这时候,可以使用以下两种方式来解决:
(1)使用 cin 语句
while (cin >> x)...
这种写法很简单,但是当输入数据达到 10 万左右时,输入时间过慢会导致超时。
(2)使用 scanf 语句
scanf 语句正常读入数据后,会返回本次读入的数据个数,例如:scanf("%d", &x); 成功读入一个整数 x 时,会返回 1;scanf("%d%d", &n, &m); 成功读入 n,m 时会返回 2;而如果读入内容为空导致失败,则会返回 EOF。
由于 OJ(Online Judge在线判题)网站使用测试数据文件自动判题,因此我们的代码实际上都是读入各个文件里的数据,当文件读完时,最后会读到文件结束符—— EOF,即 End Of File,这个符号对应整数 -1,所以,我们可以有如下 3 种写法:
第一种:
与 EOF 判断
while (scanf("%d", &x) != EOF) ...
第二种:
直接与 -1 判断
while (scanf("%d", &x) != -1) ...
第三种:
由于二进制 -1 的各个位均为 1,所以将其按位取反得到 0。
while (~scanf("%d", &x)) ...
最值问题
循环中的最值问题,需要初始化 max 以及 min 变量:
max 需要初始化为可能的最 小值,if (x > max) max = x;
min 需要初始化为可能的最 大值,if (x < min) min = x;
#include<iostream>intmain(){
int n, max = 0, min = 10000;
scanf("%d", &n);
// i + j + k == n, i > j > kfor (int i = n-3; i > 0; --i)
for (int j = i-1; j > 0; --j) {
int k = n - i - j;
if (k>0 && k<j) {
if (i < min) min = i;
if (k > max) max = k;
}
}
max == 0 ? puts("-1 -1") : printf("%d %d", min, max);
return0;
}
数位运算
这类题目通常需要使用模运算,例如:取一个数的个位,就将这个数 %10 即可。
#include<iostream>intmain(){
int n, h = 1;
scanf("%d", &n);
for (int i = n/10; i; i /= 10) h *= 10;
int m = n;
do {
n = n%10*h + n/10;
printf("%d ", n);
} while (n != m);
return0;
}
之所以使用 do{} while(); 的形式,是因为一开始 n!=m 就是不成立的,如果先判断 while(n != m),循环就会直接退出了。
while (for循环第2部分) {}
本题也可以对位数进行循环。
#include<iostream>intmain(){
int n, h = 1, w = 1;
scanf("%d", &n);
for (int i = n/10; i; i /= 10) h *= 10, ++w;
while (w--) {
n = n%10*h + n/10;
printf("%d ", n);
}
return0;
}
打印图形
#include<iostream>intmain(){
int n;
scanf("%d", &n);
//两层for循环,外层对行进行循环,内层对列进行循环for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= i; ++j)
putchar('*');//每次打印一个字符 put characterputs("");//直接换行 put string
}
return0;
}
素数
如何判断一个数是素数?——试除
如果一个大于 1 的正整数 n,从 2 开始,到 \sqrt{n} n 结束,都不能整除 n,则 n 是素数。
证明:
假设 n=a\cdot bn=a⋅b,其中 a,ba,b 为小于 nn 的正整数,且 a<=ba<=b
则有 n=a\cdot b\ge a\cdot a=a^2n=a⋅b≥a⋅a=a2,即 a\le \sqrt{n}a≤n。
因此,若 nn 不是素数,它一定有一个小于等于 \sqrt{n}n 的因子。
#include<iostream>intmain(){
int n, i;
scanf("%d", &n);
for (i = 2; i*i <= n; ++i)
if (n%i == 0) break;
if (n<2 || i*i<=n) puts("NO");
elseputs("YES");
return0;
}
可以对代码进行优化,虽然变长了,但速度更快了。
#include<iostream>intmain(){
int n;
scanf("%d", &n);
//小于2的正整数,或者大于 2 的偶数,都不是素数if (n<2 || n>2 && n%2==0) { puts("NO"); return0; }
for (int i = 3; i*i <= n; i += 2)
if (n%i == 0) { puts("NO"); return0; }
puts("YES");
return0;
}
特殊数列
迭代法求数列元素
Fibonacci 数列
#include<iostream>//全局变量默认初值为 0constint M = 10007;
intmain(){
//局部变量,默认初值不定int n, a, b, c;
a = b = 1;
scanf("%d", &n);
//当 n 为 1或2 时,进行特判if (n==1 || n==2) { printf("1"); return0; }
for (int i = 3; i <= n; ++i)
c = (a+b)%M, a = b, b = c;
printf("%d", c);
return0;
}
有时候,我们完全可以通过 赋初值来省略一些特判。
//利用变量 c 的初值可以做优化#include<iostream>constint M = 10007;
intmain(){
int n, a = 1, b = 1, c = 1;
scanf("%d", &n);
for (int i = 3; i <= n; ++i)
c = (a + b) % M, a = b, b = c;
printf("%d", c);
return0;
}
枚举
2 种思路:
(1)从 1000 ~ 10000 逐个判断,循环 9000 次,执行 36000 次模运算,较为耗时
(2)当成 4 个数来看,ijji,1\le i \le 9, 0\le j \le 9 ijji,1≤ i≤9,0≤ j≤9,执行 90 次,直接打出结果,效率高
#include<iostream>intmain(){
for(int i = 1000;i < 10000; ++i)
if(i/1000==i%10 && i/100%10==i/10%10)
printf("%d\n", i);
return0;
}
#include<iostream>intmain(){
for (int i = 1; i <= 9; ++i)
for (int j = 0; j <= 9; ++j)
printf("%d%d%d%d\n", i, j, j, i);
return0;
}
<font color=red>使用define宏定义for循环</font>
#include<iostream>#define loop(i, s, t) for(int i = s; i <= t; ++i)intmain(){
loop(i, 1, 9) loop(j, 0, 9)
printf("%d%d%d%d\n", i, j, j, i);
return0;
}
最大公约数 GCDGCD 和最小公倍数 LCMLCM
辗转相除法(欧几里得定理)
定理1:gcd(a,b)=gcd(b,a\%b) gcd( a, b)= gcd( b, a% b)
定理2:gcd(a,0)=a gcd( a,0)= a
#include<iostream>intmain(){
int a, b, t, n, m;
scanf("%d%d", &n, &m);
a = n, b = m;
while (b) t = a, a = b, b = t % b;
printf("%d", n * m / a);
return0;
}
进制转换
#include<iostream>typedeflonglong LL;
intmain(){
int n, r;
LL ans = 0, b = 1;
scanf("%d%d", &n, &r);
while (n)
ans += n % r * b, b *= 10, n /= r;
printf("%lld", ans);
return0;
}