|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "信息奥赛一本通函数题库解析-1: 求正整数2和n之间的完全数" |
| 4 | +date: 2024-07-03 00:22:51 +0800 |
| 5 | +categories: algorithms olympiad functions |
| 6 | +--- |
| 7 | + |
| 8 | +原题链接: [求正整数2和n之间的完全数](http://ybt.ssoier.cn:8088/problem_show.php?pid=1150) |
| 9 | + |
| 10 | +根据题目描述和样例输入输出的要求,我们需要编写一个程序,找到 2 和 n 之间的完全数,并按升序输出。一个完全数是指它的所有真因子(不包括自身)之和等于它本身。 |
| 11 | + |
| 12 | +以下是针对这道题的详细解题思路: |
| 13 | + |
| 14 | +1. **理解完全数的定义**:完全数是一个正整数,它等于其所有真因子(包括 1,但不包括自身)的和。例如,6 是一个完全数,因为 6 = 1 + 2 + 3。 |
| 15 | + |
| 16 | +2. **输入输出**:输入一个整数 n (n <= 5000),输出 2 到 n 之间的所有完全数,每行一个数,按从小到大的顺序排列。 |
| 17 | + |
| 18 | +3. **解决方案**: |
| 19 | + - 首先,我们需要一个函数来判断一个数是否为完全数。 |
| 20 | + - 然后,我们遍历从 2 到 n 的所有整数,使用该函数判断是否为完全数。 |
| 21 | + - 如果是完全数,将其存储在一个列表中。 |
| 22 | + - 最后,按顺序输出列表中的所有完全数。 |
| 23 | + |
| 24 | +以下是实现该方案的代码: |
| 25 | + |
| 26 | +```cpp |
| 27 | +#include <iostream> |
| 28 | +using namespace std; |
| 29 | + |
| 30 | +// 函数:判断一个数是否为完全数 |
| 31 | +bool is_perfect(int num) { |
| 32 | + if (num == 1) { |
| 33 | + return false; // 1 不是完全数 |
| 34 | + } |
| 35 | + int sum = 1; // 1 是所有数的因子 |
| 36 | + for (int i = 2; i * i <= num; ++i) { |
| 37 | + if (num % i == 0) { |
| 38 | + sum += i; |
| 39 | + if (i * i != num) { // 避免重复添加平方根 |
| 40 | + sum += num / i; |
| 41 | + } |
| 42 | + } |
| 43 | + } |
| 44 | + return sum == num; // 如果因子和等于自身,则是完全数 |
| 45 | +} |
| 46 | + |
| 47 | +int main() { |
| 48 | + int n; |
| 49 | + cin >> n; // 读入 n |
| 50 | + |
| 51 | + int perfect_numbers[5]; // 假设最多有 5 个完全数 |
| 52 | + int count = 0; // 完全数的计数 |
| 53 | + |
| 54 | + for (int i = 2; i <= n && count < 5; ++i) { |
| 55 | + if (is_perfect(i)) { |
| 56 | + perfect_numbers[count++] = i; // 如果 i 是完全数,加入数组 |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + for (int i = 0; i < count; ++i) { |
| 61 | + cout << perfect_numbers[i] << endl; // 输出所有的完全数 |
| 62 | + } |
| 63 | + |
| 64 | + return 0; |
| 65 | +} |
| 66 | +``` |
| 67 | +
|
| 68 | +### 知识点总结: |
| 69 | +
|
| 70 | +1. **因数分解**:为了求一个数的所有真因子,我们需要遍历从 1 到这个数平方根之间的所有整数。如果某个整数是这个数的因子,那么它的对应因子也可以求出。例如,12 的因子包括 1, 2, 3, 4, 6 和 12,我们只需遍历到 sqrt(12) 即可。 |
| 71 | +
|
| 72 | +2. **数据存储和排序**:使用数组或列表来存储找到的完全数,并按顺序输出。 |
| 73 | +
|
| 74 | +3. **复杂度分析**:由于我们需要遍历从 2 到 n 的所有数,并对每个数进行因数分解,时间复杂度为 O(n * sqrt(n)),对于 n <= 5000 是可以接受的。 |
| 75 | +
|
| 76 | +这段代码可以有效解决这个问题,找出所有满足条件的完全数并输出。 |
| 77 | +
|
| 78 | +### 为什么 只需遍历到 sqrt(12) 即可 |
| 79 | +
|
| 80 | +只需遍历到 \(\sqrt{n}\) 是因为在查找一个数 \(n\) 的因子时,如果 \(i\) 是 \(n\) 的一个因子,那么 \(\frac{n}{i}\) 也是 \(n\) 的一个因子。具体来说,如果我们找到一个因子 \(i\),那么我们可以直接确定另一个因子 \(\frac{n}{i}\)。 |
| 81 | +
|
| 82 | +例如,对于 \(n = 12\): |
| 83 | +- 因子对包括 (1, 12), (2, 6), 和 (3, 4)。 |
| 84 | +- 如果我们从 1 遍历到 \(\sqrt{12} \approx 3.46\),那么当我们找到因子 2 时,我们也可以确定 6 是因子(因为 \(12 / 2 = 6\))。 |
| 85 | +- 同样,当我们找到因子 3 时,我们也可以确定 4 是因子(因为 \(12 / 3 = 4\))。 |
| 86 | +
|
| 87 | +这就意味着,我们只需遍历到 \(\sqrt{n}\) 就可以找到所有的因子对,因为对于每一个 \(i \leq \sqrt{n}\),对应的 \(\frac{n}{i}\) 都会被找到。 |
| 88 | +
|
| 89 | +这个方法大大减少了需要检查的数的数量,降低了时间复杂度。遍历到 \(\sqrt{n}\) 的时间复杂度是 \(O(\sqrt{n})\),而不是遍历到 \(n\) 的 \(O(n)\),这对于较大的 \(n\) 来说是非常有效的。 |
| 90 | +
|
| 91 | +让我们再来看一遍代码中的这部分: |
| 92 | +
|
| 93 | +```cpp |
| 94 | +for (int i = 2; i * i <= num; ++i) { |
| 95 | + if (num % i == 0) { |
| 96 | + sum += i; |
| 97 | + if (i * i != num) { // 避免重复添加平方根 |
| 98 | + sum += num / i; |
| 99 | + } |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +这里的 `i * i <= num` 就是为了遍历到 \(\sqrt{num}\)。如果 `i` 是 `num` 的因子,我们将 `i` 和 `num / i` 都加到因子和 `sum` 中。 |
| 105 | + |
| 106 | +避免重复添加平方根的目的是处理完全平方数的情况。例如,16 的因子包括 1, 2, 4, 8 和 16,但我们不希望将 4 添加两次。 |
| 107 | + |
| 108 | +通过这种方法,我们能够高效地找到所有因子并判断一个数是否为完全数。 |
0 commit comments