一、选秀节目评委打分
1.使用for循环
#include <stdio.h>
int main() {
float sum = 0, score;
int i;
printf("请输入10个评委的分数:\n");
for (i = 0; i < 10; i++) {
scanf("%f", &score);
sum += score;
}
printf("平均分:%.2f\n", sum / 10);
return 0;
}
2.使用while循环
#include <stdio.h>
int main() {
float sum = 0, score;
int i = 0;
printf("请输入10个评委的分数:\n");
while (i < 10) {
scanf("%f", &score);
sum += score;
i++;
}
printf("平均分:%.2f\n", sum / 10);
return 0;
}
3.使用do-while循环
#include <stdio.h>
int main() {
float sum = 0, score;
int i = 0;
printf("请输入10个评委的分数:\n");
do {
scanf("%f", &score);
sum += score;
i++;
} while (i < 10);
printf("平均分:%.2f\n", sum / 10);
return 0;
}
二、车牌号穷举
这道题有不同的解法,这里先给出相对简单的一种。
#include <stdio.h>
int main() {
printf("可能的车牌号有:\n");
// 遍历所有4位数(1000~9999)
for (int num = 1000; num <= 9999; num++) {
int A = num / 1000; // 第1位
int B = (num / 100) % 10; // 第2位
int C = (num / 10) % 10; // 第3位
int D = num % 10; // 第4位
// 检查条件:
// 1. 前两位相同(A == B)
// 2. 后两位之和为6(C + D == 6)
// 3. 能被2整除(num % 2 == 0)
if (A == B && C + D == 6 && num % 2 == 0) {
printf("%d\n", num);
}
}
return 0;
}
接下来介绍一种实际编程中大概率会用到的方法,球球在这里先指明两种方法的优劣。
1.相较于第一种方法,第二种方法可以极大的减少计算数量从而提高程序运行效率。
2.从计算车牌号的角度出发,第一种方法仅仅只是穷举了从 1000 到 9999 的所有车牌号,也就是说实际运行过程中进行了9000次计算,下面的第二种方法只需进行 【9 × 7 = 63】 次计算!理论上提升了大约143倍的运行效率,真是非常恐怖!!!
代码如下:
#include <stdio.h>
int main() {
printf("可能的车牌号有:\n");
// 遍历前两位 A(1~9)
for (int A = 1; A <= 9; A++) {
// 遍历后两位 C 和 D,满足 C + D = 6 且 D 是偶数
for (int C = 0; C <= 6; C++) {
int D = 6 - C;
// 检查 D 是否为偶数
if (D % 2 == 0) {
// 构造车牌号
int plate = A * 1000 + A * 100 + C * 10 + D;
printf("%d\n", plate);
}
}
}
return 0;
}
球球建议先自行理解,实在想不通再看下面隐藏起来的解释。
优化思路
- 前两位相同(甲的条件):
• 设车牌号为 A A C D
(即 A == B
)。
• A
的取值范围是 1~9
(因为车牌号是4位数,首位不能为0)。
- 后两位之和为6(乙的条件):
• C + D = 6
,其中 C
和 D
都是 0~9
的数字。
• 可能的组合:(0,6)
, (1,5)
, (2,4)
, (3,3)
, (4,2)
, (5,1)
, (6,0)
。
- 能被2整除(丙的条件):
• 车牌号 A A C D
必须能被 2
整除,即 D
必须是偶数(D ∈ {0, 2, 4, 6, 8}
)。
• 因此,(C, D)
的有效组合为:(0,6)
, (2,4)
, (4,2)
, (6,0)
。
三、韩信点兵
这道题同样有两种解法,两种解法的效果同上。球球先给出较易理解的穷举法。
#include <stdio.h>
int main() {
int a, b, c;
printf("请输入 a, b, c(分别代表 3人一排、5人一排、7人一排的排尾人数):");
scanf("%d %d %d", &a, &b, &c);
printf("可能的士兵人数(1000~1500):\n");
int found = 0; // 标记是否找到解
// 遍历 1000~1500,寻找满足条件的 N
for (int N = 1000; N <= 1500; N++) {
if (N % 3 == a && N % 5 == b && N % 7 == c) {
printf("%d\n", N);
found = 1;
}
}
if (!found) {
printf("无解!请检查输入是否合法。\n");
}
return 0;
}
下面给出一种优化算法,同上题,可减少计算量,同时适用于求解更大的N(也就是总人数)
具体实现原理为中国剩余定理
#include <stdio.h>
int main() {
int a, b, c;
printf("请输入 a, b, c(0 ≤ a < 3, 0 ≤ b < 5, 0 ≤ c < 7):");
scanf("%d %d %d", &a, &b, &c);
// 计算最小解 N0(0 ≤ N0 < 105)
int N0 = -1;
for (int n = 0; n < 105; n++) {
if (n % 3 == a && n % 5 == b && n % 7 == c) {
N0 = n;
break;
}
}
if (N0 == -1) {
printf("无解!请检查输入是否合法。\n");
return 0;
}
printf("可能的士兵人数(1000~1500):\n");
int found = 0;
// 计算所有可能的 N = 105k + N0,且在 1000~1500 范围内
for (int k = (1000 - N0) / 105; 105 * k + N0 <= 1500; k++) {
int N = 105 * k + N0;
if (N >= 1000 && N <= 1500) {
printf("%d\n", N);
found = 1;
}
}
if (!found) {
printf("无解!\n");
}
return 0;
}
这题有些难度,下面球球尽最大努力给出详细解释。
那我问你,高考过去那么长时间了,有些数学定理难免会忘,那我问你,中国剩余定理是什么?(球球的头怎么尖尖的,球体要变成圆锥了QAQ)
中国剩余定理(Chinese Remainder Theorem, CRT)
中国剩余定理是数论中的一个重要定理,用于求解同余方程组。它最早出现在中国古代数学著作《孙子算经》(约公元3-5世纪)的“物不知数”问题中,后来被欧洲数学家推广并命名为“中国剩余定理”。
1. 问题描述
两两互质的正整数 n_1, n_2, \dots, n_k ,以及余数 a_1, a_2, \dots, a_k ,求解满足以下同余方程组的整数 x :
\begin{cases}
x \equiv a_1 \pmod{n_1} \\
x \equiv a_2 \pmod{n_2} \\
\vdots \\
x \equiv a_k \pmod{n_k}
\end{cases}
定理保证:如果 n_1, n_2, \dots, n_k 两两互质,那么这个方程组在模 N = n_1 n_2 \dots n_k 下有唯一解。
2. 韩信点兵问题的数学表示
在韩信点兵问题中:
• 士兵总数 x 满足:
\begin{cases}
x \equiv a \pmod{3} \\
x \equiv b \pmod{5} \\
x \equiv c \pmod{7}
\end{cases}
• 由于 3, 5, 7 两两互质,中国剩余定理适用。
3. 中国剩余定理的构造性解法
步骤 1:计算 N = n_1 n_2 \dots n_k
在韩信点兵问题中:
N = 3 \times 5 \times 7 = 105
所有解的形式为:
x = 105k + x_0 \quad (k \in \mathbb{Z})
其中 x_0 是最小的非负解( 0 \leq x_0 < 105 )。
步骤 2:计算 x_0
我们需要找到一个 x_0 满足:
\begin{cases}
x_0 \equiv a \pmod{3} \\
x_0 \equiv b \pmod{5} \\
x_0 \equiv c \pmod{7}
\end{cases}
(1) 先解前两个方程
设 x_0 = 5m + b ,代入第一个方程:
5m + b \equiv a \pmod{3} \\
\Rightarrow 2m \equiv a - b \pmod{3} \\
\Rightarrow m \equiv (a - b) \times 2^{-1} \pmod{3}
由于 2^{-1} \equiv 2 \pmod{3} (因为 2 \times 2 = 4 \equiv 1 \pmod{3} ),所以:
m \equiv 2(a - b) \pmod{3}
取 m = 3k + 2(a - b) ,代入 x_0 :
x_0 = 5(3k + 2(a - b)) + b = 15k + 10(a - b) + b
取 k = 0 ,得到:
x_0 = 10a - 9b
(2) 代入第三个方程
x_0 \equiv c \pmod{7} \\
10a - 9b \equiv c \pmod{7} \\
\Rightarrow 3a - 2b \equiv c \pmod{7} \quad (\text{因为 } 10 \equiv 3, -9 \equiv -2 \pmod{7})
检查是否成立:
• 如果成立,则 x_0 = 10a - 9b 是一个解。
• 如果不成立,说明输入 a, b, c 不合法(无解)。
(3) 调整 x_0 到 0 \leq x_0 < 105
由于 x_0 = 10a - 9b 可能是负数或大于 105,我们需要调整:
x_0 = (10a - 9b) \mod 105
步骤 3:求所有解
所有解的形式为:
x = 105k + x_0 \quad (k \in \mathbb{Z})
在韩信点兵问题中, x 的范围是 1000 \leq x \leq 1500 ,所以:
1000 \leq 105k + x_0 \leq 1500
解不等式:
k_{\text{min}} = \left\lceil \frac{1000 - x_0}{105} \right\rceil \\
k_{\text{max}} = \left\lfloor \frac{1500 - x_0}{105} \right\rfloor
然后遍历 k 从 k_{\text{min}} 到 k_{\text{max}} ,计算 x = 105k + x_0 。
4. 示例
输入 1:合法输入
a=2, b=3, c=2
计算:
x_0 = 10 \times 2 - 9 \times 3 = 20 - 27 = -7 \equiv 98 \pmod{105}
检查:
98 \equiv 0 \pmod{7} \quad (\text{但 } c=2 \text{,矛盾})
实际上,正确的 x_0 应满足:
10a - 9b \equiv c \pmod{7} \\
20 - 27 \equiv 2 \pmod{7} \\
-7 \equiv 0 \equiv 2 \pmod{7} \quad (\text{不成立})
因此输出:
无解!请检查输入是否合法。
输入 2:合法输入
a=1, b=2, c=3
计算:
x_0 = 10 \times 1 - 9 \times 2 = 10 - 18 = -8 \equiv 97 \pmod{105}
检查:
97 \equiv 6 \pmod{7} \quad (\text{但 } c=3 \text{,矛盾})
因此输出:
无解!请检查输入是否合法。
输入 3:合法输入
a=2, b=1, c=2
计算:
x_0 = 10 \times 2 - 9 \times 1 = 20 - 9 = 11 \equiv 11 \pmod{105}
检查:
11 \equiv 4 \pmod{7} \quad (\text{但 } c=2 \text{,矛盾})
因此输出:
无解!请检查输入是否合法。
5. 总结
关键点 | 说明 |
适用条件 | 模数 n_1, n_2, \dots, n_k 两两互质 |
解的形式 | x = Nk + x_0 ,其中 N = n_1 n_2 \dots n_k |
最小解 x_0 | 通过逐步代入计算 |
解的调整 | 确保 x_0 在 [0, N) 范围内 |
无解情况 | 如果 x_0 不满足所有方程,则无解 |
球球补充一下,中国剩余定理在密码学、计算机科学(如RSA算法(RSA也是网安下学期的专业课哦))和工程计算中都有有广泛应用。
四、数字枚举
#include <stdio.h>
int main() {
int count = 0; // 计数器,记录符合条件的3位数个数
printf("所有可能的3位数:\n");
// 百位数 i (1~4)
for (int i = 1; i <= 4; i++) {
// 十位数 j (1~4),且 j ≠ i
for (int j = 1; j <= 4; j++) {
if (j == i) continue; // 跳过重复数字
// 个位数 k (1~4),且 k ≠ i 且 k ≠ j
for (int k = 1; k <= 4; k++) {
if (k == i || k == j) continue; // 跳过重复数字
// 生成3位数:i*100 + j*10 + k
int num = i * 100 + j * 10 + k;
printf("%d\n", num);
count++;
}
}
}
printf("\n总计:%d 个\n", count);
return 0;
}
当然这道题也有更高效的算法,比如改用递归回溯法,但是已经严重超出这本书的涵盖范围,球球在这里不做演示,感兴趣的话可以自己研究一下(绝对不是球球自己也不会啊,绝对不是ο(=•ω<=)ρ
五、九九乘法表
球球自己加了一点小装饰,嘿嘿,不喜欢的话直接删掉就行啦~
#include <stdio.h>
int main() {
printf("========== 九九乘法表 ==========\n");
for (int i = 1; i <= 9; i++) { // 外层循环控制行(乘数1)
for (int j = 1; j <= i; j++) { // 内层循环控制列(乘数2)
printf("%d×%d=%-2d ", j, i, i * j); // 格式化输出
}
printf("\n"); // 每行结束后换行
}
printf("===============================\n");
return 0;
}
本次作业难度较大,以上内容球球如果有任何问题多多在评论区指出哦~
谢谢大家啦~