👋 欢迎来到“C语言基础应用实战”篇!
在这一篇博客中,我们将通过一个经典的编程问题来巩固你对数组、循环和条件语句的理解:开灯问题。问题的核心是模拟一个操作过程,并通过逻辑推导来得出最终的状态。通过这个问题,你将学会如何使用 C 语言来解决这类有规律的模拟问题。
🌟 问题描述
有 n 盏灯,编号为 1~n,初始时所有灯都关闭。现在有 k 个人依次进行操作。每个操作的规则是:
1. 第 1 个人将所有灯打开。
2. 第 2 个人按下所有编号为 2 的倍数的开关(这些灯将被关掉)。
3. 第 3 个人按下所有编号为 3 的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭)。
4. 依此类推,第 i 个人按下所有编号为 i 的倍数的开关。最终,我们需要输出哪些灯是开着的。
输入
- 两个整数 n 和 k,其中 1 ≤ k < n ≤ 1000,分别表示灯的数量和进行操作的人数。
输出
- 输出所有开着的灯的编号,按升序排列。
🎯 解题思路
这个问题的核心是模拟多次开关操作。我们可以使用一个长度为 n 的数组来表示每盏灯的开关状态。初始化时,所有灯的状态为 0(表示关),然后根据规则模拟每个人的操作。
1. 数组表示灯的状态
我们使用一个数组 `a[]` 来存储灯的状态,数组的每个元素代表一个灯:
- `a[i]` 为 1 时表示第 i 盏灯是开的;
- `a[i]` 为 0 时表示第 i 盏灯是关的。
2. 每个人的操作
- 第 1 个人将所有灯打开,所以我们把数组中的每个元素都设置为 1。
- 对于第 i 个人,他会操作所有编号为 i 的倍数的灯(即 `j % i == 0` 的灯),对这些灯执行“开关”操作。
- 我们可以通过 `a[j] = !a[j]` 来实现开关操作(若灯是关的,变为开;若灯是开的,变为关)。
3. 输出开着的灯
最后,我们遍历数组,输出所有值为 1 的灯的编号。
📝 代码实现
#include <stdio.h>
#include <string.h>
#define N 1010 // 定义数组的最大长度
int a[N]; // 存储灯的状态
int main() {
int n, k, first = 1;
scanf("%d%d", &n, &k); // 输入灯的数量和进行操作的人数
memset(a, 0, sizeof(a)); // 初始化灯状态数组,0表示关
// 模拟每个人的操作
for (int i = 1; i <= k; i++) {
for (int j = 1; j <= n; j++) {
if (j % i == 0) {
a[j] = !a[j]; // 每次按下开关,改变灯的状态
}
}
}
// 输出所有开着的灯
for (int i = 1; i <= n; i++) {
if (a[i]) { // 如果灯是开的
if (first) {
first = 0; // 第一个输出前不加空格
} else {
printf(" "); // 后续输出灯号时前面加空格
}
printf("%d", i); // 输出开着的灯的编号
}
}
return 0;
}
💡 代码分析
1. 初始化数组
memset(a, 0, sizeof(a)); // 将数组 a 中的所有元素初始化为 0,表示所有灯最开始都是关的
tips:使用 `memset` 函数来初始化数组,这是一个简洁且高效的做法。`memset` 会将数组中的所有字节设置为给定的值,这里将所有元素初始化为 0。
2. 模拟开关操作
a[j] = !a[j]; // 如果灯是关的,变为开;如果灯是开的,变为关
通过对每个操作进行模拟,当编号为 `i` 的人按下开关时,我们对所有编号为 `i` 的倍数的灯执行切换操作。
3. 输出开着的灯
if (first) {
first = 0; // 第一个输出时不加空格
} else {
printf(" "); // 后续输出时加空格
}
使用 `first` 标志量来控制是否在输出灯编号前加空格,确保输出的格式符合要求,即灯编号之间用空格分隔。
🏆 复杂度分析
- 时间复杂度:
:每个人的操作需要遍历 `n` 个灯,总共进行 `k` 轮操作。
- 空间复杂度:
:我们使用了一个大小为 `n` 的数组来存储每盏灯的状态。
由于 `n` 和 `k` 都小于 1000,因此这个算法的效率是可以接受的。
🌟 示例
输入
7 3
输出
1 5 6 7
解析
- 第 1 个人操作:所有灯都打开。
- 第 2 个人操作:关闭编号为 2 的倍数的灯(2, 4, 6)。
- 第 3 个人操作:切换编号为 3 的倍数的灯(3, 6)。
最终,开着的灯编号为 1, 5, 6, 7。
✨ 总结
1. 开关问题的模拟:通过数组模拟每个灯的开关操作,可以清晰地理解问题的解决过程。
2. 标志量的使用:在输出时使用标志量控制格式,避免不必要的空格。
3. 效率分析:通过分析时间复杂度和空间复杂度,确认了该算法适用于题目中给出的范围。
✍️ 读者互动
- 👉 你认为如果增加更多的人操作,算法是否还有效?
- 👉 你有没有思考过其他可能的优化方式?
欢迎在评论区分享你的想法和优化方案!让我们一起深入探讨有趣的编程问题! 🤝