枚举法
枚举法
理解枚举算法的原理及特点,可以解决实际问题。
枚举法在候选集合有限且不大时,按固定顺序把每一个可能解都拿出来检验,留下满足条件的那些。原理是穷尽而不是猜测;特点是思路直、正确性好写,代价是候选变多时循环层数或范围会爆炸。三级常见形态是一重或两重循环:枚举一个整数、一对因子、或数组里的一个下标。先确定「枚举什么、范围从哪到哪、判定条件是什么」,再写循环,比先写代码再补边界更稳。
客观题会问某段枚举循环执行了多少次、会不会漏解或重解。编程题则直接要求找出所有满足等式或约束的解,例如统计有多少组正整数之和为给定值。认证标准规定考试时长为 120 分钟,两道编程各占 25 分,枚举题务必先估算循环次数,避免无意义的三层循环。
// 枚举 1..n 中有多少个数,各位数字之和等于 k
int count = 0;
for (int x = 1; x <= n; ++x) {
int t = x, s = 0;
while (t) { s += t % 10; t /= 10; }
if (s == k) ++count;
}
范围用 < 还是 <=、是否允许重复、是否要求有序,都必须对照题意写死,不能「看起来合理就停」。
著名的哥德巴赫猜想:任一大于 2 的偶数都可写成两个素数之和。我们可以通过枚举法来证明它。
小猫分鱼
C++时间 1 s内存 512 MB标准输入输出
海滩上有一堆鱼,N 只小猫来分。第一只小猫把这堆鱼平均分为 N 份,多了 i<N 个,这只小猫把多的 i 个扔入海中,拿走了一份。第二只小猫接着把剩下的鱼平均分成 N 份,又多了 i 个,小猫同样把多的 i 个扔入海中,拿走了一份。第三、第四、……,第 N 只小猫仍是最终剩下的鱼分成 N 份,扔掉多了的 i 个,并拿走一份。
编写程序,输入小猫的数量 N 以及每次扔到海里的鱼的数量 i,输出海滩上最少的鱼数,使得每只小猫都可吃到鱼。
例如:两只小猫来分鱼 N=2,每次扔掉鱼的数量为 i=1,为了每只小猫都可吃到鱼,可令第二只小猫需要拿走 1 条鱼,则此时待分配的有 3 条鱼。第一只小猫待分配的鱼有 3×2+1=7 条。
输入格式
总共 2 行。第一行一个整数 N,第二行一个整数 i。
保证 0<N<10;i<N 。
输出格式
一行一个整数,表示满足要求的海滩上最少的鱼数。
样例
2 1
7
3 1
25
样例解释 2
三只小猫来分鱼 N=3,每次扔掉鱼的数量为 i=1,为了每只小猫都可吃到鱼,可令第三只小猫需要拿走 3 条鱼(拿走 1 条和 2 条不满足要求),则此时待分配的有 10 条鱼。第二只小猫待分配的鱼有 10×3/2+1=16 条。第一只小猫待分配的鱼有 16×3/2+1=25 条。
你的代码(C++)
提交后由评测机编译运行,结果见下方76 B / 64 KB
判题结果
下面 C++ 代码可以计算 1 到 100 的累加和,采用的是穷举法。
int main()
{
int i,sum=0;
for(int i = 1; i <= 100 ; i++)
sum += i;
cout << sum << endl;
cout << endl;
return 0;
}
完全平方数
C++时间 1 s内存 512 MB标准输入输出
小杨同学有一个包含 n 个非负整数的序列 A,他想要知道其中有多少对下标组合 ⟨i,j⟩(1≤i<j≤n),使得 Ai+Aj 是完全平方数。
如果 x 是完全平方数,则存在非负整数 y 使得 y×y=x。
输入格式
第一行一个非负整数 n,表示非负整数个数。
第二行包含 n 个非负整数 A1,A2,…An,表示序列 A 包含的非负整数。
输出格式
输出一行一个整数表示答案。
样例
5 1 4 3 3 5
3
数据范围
对于全部数据,保证有 1≤n≤1000,0≤Ai≤105。
原文保真说明:来源改写 1 处(照原卷保留,未作修改)
来源题面被改写错字输入格式
原卷作
第二行包含 $n$ 个非负整数,常规写法应为第二入行包含 $n$ 个非负整数洛谷 B3957 输入格式 OCR 作「第二入行」;官方 PDF 第 8 页作「第二行」,已恢复。其余题面/样例仍以洛谷为准。
已按官方原件恢复:现为原卷原文
你的代码(C++)
提交后由评测机编译运行,结果见下方76 B / 64 KB
判题结果
把整数 3025 从中剪开分为 30 和 25 两个数,此时再将这两数之和平方,计算结果又等于原数。(30+25)×(30+25)=55×55=3025,这样的数叫“雷劈数”。可以使用枚举的方法求出所有符合这样条件的四位数。
有 A B C D 4 个人,其中一个是小偷,每个人说一句话,只有小偷说的是假话,其中 A 说:不是我。B 说:是 C。C 说:是 D。D 说:C 瞎说,请问谁是小偷( )。
回文拼接
C++时间 1 s内存 512 MB标准输入输出
一个字符串是回文串,当且仅当该字符串从前往后读和从后往前读是一样的,例如,aabaa 和 ccddcc 都是回文串,但 abcd 不是。
小杨有 n 个仅包含小写字母的字符串,他想请你编写程序判断每个字符串是否由两个长度至少为 2 的回文串前后拼接而成。
输入格式
第一行包含一个正整数 n,代表字符串数量。
接下来 n 行,每行一个仅包含小写字母的字符串。
输出格式
对于每个字符串输出一行,如果该字符串由两个长度至少为 2 的回文串前后拼接而成则输出 Yes,否则输出 No。
样例
4 abcd aabbb aaac abcdd
No Yes No No
样例 1 解释
对于第 1,3,4 个字符串,都不是由两个长度至少为 2 的回文串前后拼接而成。
第 2 个字符串由回文串 aa 和 bbb 前后拼接而成,并且两个回文串长度都至少为 2。
数据规模与约定
对全部的测试数据,保证 1≤n≤10,且每个字符串的长度均不超过 100。
数据范围
对于全部数据,保证有 1≤n≤10,并且每个字符串的长度不超过 100。
你的代码(C++)
提交后由评测机编译运行,结果见下方76 B / 64 KB
判题结果
想要计算从数字 n 到数字 m 之间(包含 n 和 m )有多少个数字 d 出现,下列程序哪个能够实现( )。
A.
int n,m,res,d;
cin >> n >> m>>d;
for(int i = n+1; i <= m; i++){
int temp = i;
while(temp){
if(temp % 10 == d) res++;
temp /= 10;
}
}
cout << res << endl;
return 0;
B.
int n,m,res,d;
cin >> n >> m>>d;
for(int i = n; i <= m; i++){
int temp = i;
while(temp){
if(temp % 10 = d) res++;
temp /= 10;
}
}
cout << res << endl;
return 0;
C.
int n,m,res=0,d;
cin >> n >> m>>d;
for(int i = n; i <= m; i++){
int temp = i;
while(temp){
if(temp % 10 == d) res++;
temp /= 10;
}
}
cout << res << endl;
return 0;
D.
int n,m,res=0,d;
cin >> n >> m>>d;
for(int i = n; i <= m; i++){
while(temp){
if(temp % 10 == d) res++;
temp /= 10;
}
}
cout << res << endl;
return 0;
原文保真说明:来源改写 1 处(照原卷保留,未作修改)
来源题面被改写数值印错题干
原卷作
n+1洛谷有题将选项 A 的 n+1 写成 C1;已按官方 PDF 第 4 页恢复。
已按官方原件恢复:现为原卷原文
兔子五元一只,鸡三元一只,小鸭子一元三只,现在你有一百元要买一百只,兔子、鸡、鸭子,问兔子、鸡、鸭子各买多少只,每种必须至少一只,下列哪个程序能实现。( )。
A.
for(int i = 0; i <= 100; i++){
for(int j = 0; j <= 100; j++){
for(int k = 0; k <= 100; k++){
if(i + j + k == 100 && 25 * i + 10 * j + k == 300)
cout << i << " " << j << " " << k << endl;
}
}
}
B.
for(int i = 0; i <= 100; i++){
for(int j = 0; j <= 100; j++){
for(int k = 0; k <= 100; k++){
if(i + j + k == 100 && 20 * i + 10 * j + k == 300)
cout << i << " " << j << " " << k << endl;
}
}
}
C.
for(int i = 0; i <= 20; i++){
for(int j = 0; j <= 34; j++){
for(int k = 0; k <= 20; k++){
if(i + j + k == 100 && 15 * i + 9 * j + k == 300)
cout << i << " " << j << " " << k << endl;
}
}
}
D.
for(int i = 0; i <= 100; i++){
for(int j = 0; j <= 100; j++){
for(int k = 0; k <= 100; k++){
if(i + j + k == 100 && 15 * i + 9 * j + k == 300)
cout << i << " " << j << " " << k << endl;
}
}
}
下面枚举法查找最大值索引程序中,横线处应该填写的是( )
#include <iostream>
using namespace std;
int main() {
int arr[] = {3, 7, 2, 9, 5};
int maxIndex = 0;
for (int i = 1; i < 5; i++) {
_______________________
{
maxIndex = i;
}
}
cout << maxIndex;
return 0;
}
有 n 个正整数,假设一个正整数是美丽数字当且仅当该正整数是 9 的倍数但不是 8 的倍数。下面的程序是编写计算 n 个正整数中美丽数字的数量,横线处应该填入的是( )
for (int i = 1; i <= n; i++) {
cin >> a;
_______________________
cnt++;
}
以下程序使用枚举法(穷举法)求解满足条件的三位数,横线处应该填入的是( )
#include <iostream>
using namespace std;
int main() {
int count = 0;
for (int i = 100; i <= 999; i++) {
int a = i / 100;
________________
int c = i % 10;
if (a * a + b * b == c * c) {
count++;
}
}
cout << count << endl;
return 0;
}
以下问题中,最不适合用枚举法解决的是:( )
用枚举法解决“鸡兔同笼问题:头共 35 个,脚共 94 只,求鸡和兔的数量”,以下枚举逻辑最合理的是:( )
判断某个数是否是质数,枚举范围可适当缩小(遍历到 i 而非 i),提升效率。
原文保真说明:来源改写 1 处(照原卷保留,未作修改)
来源题面被改写数值印错题干
原卷作
$\sqrt{i}$ 而非 $i$,常规写法应为$\sqrt{n}$ 而非 $n$初稿按常规范式写成 √n;官方 PDF 第 4 页公式变量为 i,已按 600 dpi 宽裁恢复。
已按官方原件恢复:现为原卷原文