CSP-J 入门级 · CSP 2022 入门级第一轮 第 19 题

完善程序 · 答案 ABCD · 考点:枚举法、整数与整除

← 整套速查表 CSP-J 入门级 全部真题

题目

三、完善程序(单选题,每小题 $3$ 分,共计 $30$ 分)

(1)(枚举因数)从小到大打印正整数 $n$ 的所有正因数。

试补全枚举程序。

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;

    vector<int> fac;
    fac.reserve((int)ceil(sqrt(n)));

    int i;
    for (i = 1; i * i < n; ++i){
        if (①){
            fac.push_back(i);
        }
    }

    for (int k = 0; k < fac.size(); ++k){
        cout << ② << "";
    }
    if (③) {
        cout << ④ << "";
    }
    for (int k = fac.size() - 1; k >= 0; --k){
        cout << ⑤ << "";
    }
}

①~⑤处应填( )

答案:ABCD

考点

枚举法 讲解 →枚举法 真题汇总 →整数与整除

解题思维

这题核心就是枚举因数,只需循环到 sqrt(n)。小于 sqrt(n) 的因子存起来,大于 sqrt(n) 的因子用 n/因子对称得到,中间若遇到完全平方数单独处理一次,最后按从小到大输出即可。

常见错误

最容易错的是循环条件写成 i*i<=n,这样会把 sqrt(n) 重复存入 fac,导致后面输出时 sqrt(n) 出现两次;还有完全平方数的判断时机,必须在循环结束后用当前的 i 去判,而不是用 i-1。另外输出顺序一定要严格从小到大,先输出 fac 里的,再判中间,最后倒序输出 n/fac,否则直接丢分。

详细解析(节选)

考点定位:这是一道典型的枚举因数题,考查循环边界控制、整除判断、完全平方数特殊处理以及输出顺序。在 CSP 入门级里,这类题每年必考,2022 年就是 1 题,但枚举法在题库里总共出现过 159 次,覆盖从 2009 到 2026 年的所有级别,尤其是 CSP 入门级就占了 133 题,说明命题人特别爱在基础阶段挖这个坑。整数整除判断则考了 9 次,其中入门级就 3 次,可见这是基本功中的基本功。命题人往往喜欢把循环条件、判断对象、输出逻辑拆成几个空,让学生一步步去验证,而不是直接给代码。本题就是把循环条件、判断对象、完全平方数判断、中间因子输出、对称因子输出这五个环节拆开,考的就是你是否真的理解枚举因数的对称性。

思路拆解:我们要从小到大打印 n 的所有正因数。直接枚举 1 到 n 太慢,所以只枚举到 sqrt(n)。对于每个 i,如果 i 能整除 n,那么 i 和 n/i 都是因数。当 i < sqrt(n) 时,i 和 n/i 是两个不同的数,i 小 n/i 大;当 i = sqrt(n) 时,i 和 n/i 是同一个数,只算一次。因此程序分三步:先收集所有小于 sqrt(n) 的因数,再判断 sqrt(n) 是否恰好是整数(完全平方数),最后倒序输出对应的较大因数。

逐空推理:

第一个空在循环条件里:for (i = 1; i * i < n; ++i)。这里故意写成 i*i < n,意味着循环只执行到 i 满足 i*i < n 的最大整数,也就是 i < sqrt(n)。所以 i 永远不会等于 sqrt(n),sqrt(n) 不会在循环里被处理。那么循环体内只需要判断 i 是否能整除 n,即 n % i == 0。选项 B 是 n % i == 1,这永远不成立(余数只能是 0 或大于 0 的数,不可能等于 1 且同时整除),选项 C 和 D 把 i-1 当成除数,逻辑完全跑偏。因此①只能选 A:n % i == 0。

第二个空是输出 fac 里的元素:for (int k = 0; k < fac.size(); ++k) cout << ② << " "; fac 里存的是所有小于 sqrt(n) 的因数,按从小到大的顺序,所以直接输出 fac[k] 即可。选项 A 是 n / fac[k],这是大于 sqrt(n) 的因数,放这里会乱序;选项 C 和 D 更是莫名其妙。所以②选 B:fac[k]。

第三个空是判断完全平方数:循环结束后,i 已经停在第一个不满足 i*i < n 的值,也就是 i*i >= n。如果 n 是完全平方数,那么 i*i 一定等于 n;如果不是,i*i 一定大于 n。因此判断条件应该是 i*i == n。选项 A 用 i-1,选项 B 用 (i-1)*i,选项 D 用 i*(i-1),这些都不对。所以③选 C:i*i == n。

第四个空是输出完全平方数时的中间因数:如果 i*i == n 成立,说明 sqrt(n) = i,这个数还没输出过,要单独输一次。因为 fac 里只存了小于 sqrt(n) 的因数,i 本身不在 fac 里,所以直接输出 i。选项 A 是 n-i,选项 B 是 n-i+1,选项 C 是 i-1,都不对。所以④选 D:i。

第五个空是输出大于 sqrt(n) 的因数:fac 里的元素从小到大,对应的 n/fac[k] 就是从大到小,所以要倒序遍历 fac,输出 n / fac[k]。选项 B 是 fac[k],那是小于 sqrt(n) 的,放这里会重复;选项 C 和 D 同样逻辑错误。所以⑤选 A:n / fac[k]。

验证示例:拿 n = 12 跑一遍。循环 i=1,2,3 时 i*i < 12 成立,且 12%1=0,12%2=0,12%3=0,所以 fac 里依次 push 1,2,3。i=4 时 4*4=16 > 12,循环结束。此时 i=4,判断 i*i==12 不成立,不输出中间值。倒序输出 n/fac[k]:12/3=4,12/2=6,12/1=12。最终结果 1 2 3 4 6 12,完全正确。

易错点再强调:很多学生会把循环条件写成 i*i <= n,这样 i 会走到 sqrt(n),在循环里就把 sqrt(n) 也 push 进了 fac,导致后面输出时 sqrt(n) 出现两次,或者判断 i*i==n 时 i 已经变了,逻辑全乱。还有学生会在判断完全平方数时用 i-1,因为循环结束时 i 是第一个不满足 i*i < n 的数,i-1 才是最后一个满足的数,但 i-1 不一定等于 sqrt(n),比如 n=12,i=4,i-1=3,3*3=9≠12,用 i-1 判断永远错。这些坑都是命题人精心设计的,必须细心。

考频与趋势:枚举法在题库里总共 159 次,逐年分布显示 2026 年考了 13 题,2025 年 6 题,2024 年 6 题,2023 年 5 题,说明近五年枚举类题目明显增多,命题趋势是往基础算法回归。整数整除考了 9 次,入门级占 3 次,说明这类基础判断依然是高频考点。基于这个趋势,未来 CSP 入门级很可能继续加大枚举、整除、简单循环控制的比重,甚至可能出现更复杂的枚举优化题(比如枚举约数和、枚举质因数等)。暂无足够依据预测具体年份的题目形式,但可以肯定的是,掌握枚举因数的对称性和边界处理是必须的基础。

总结:这道题看似简单,实则把枚举因数的核心逻辑拆成了五个细节,考的就是你是否真正理解循环边界、整除判断、完全平方数处理以及输出顺序。只要抓住“小于 sqrt(n) 存,等于 sqrt(n) 单独判,大于 sqrt(n) 对称出”这一主线,逐个空验证,就能稳稳拿分。

同考点真题

在 OI伴学 里做这道题
登录后可计时作答、查看完整六段式解析,并按你的错题自动生成同类练习。
去刷题先做一次 AI 学情诊断