题解:洛谷 P2141 珠心算测验
【题目来源】
洛谷:P2141 [NOIP 2014 普及组] 珠心算测验 - 洛谷
【题目描述】
珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练,既能够开发智力,又能够为日常生活带来很多便利,因而在很多学校得到普及。
某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他随机生成一个正整数集合,集合中的数各不相同,然后要求学生回答:其中有多少个数,恰好等于集合中另外两个(不同的)数之和?
最近老师出了一些测验题,请你帮忙求出答案。
【输入】
共两行,第一行包含一个整数 \(n\),表示测试题中给出的正整数个数。
第二行有 \(n\) 个正整数,每两个正整数之间用一个空格隔开,表示测试题中给出的正整数。
【输出】
一个整数,表示测验题答案。
【输入样例】
4
1 2 3 4
【输出样例】
2
【核心思想】
-
问题分析:给定一个由 \(n\) 个互不相同正整数组成的集合,要求统计集合中有多少个数可以表示为集合中另外两个不同数之和。本质是一个两数之和存在性问题,需要避免重复计数(即一个数即使有多种拆分方式,也只计一次)。
-
算法选择:
- 标记数组 + 枚举:由于集合元素数量 \(n \le 100\),且每个数的大小有范围限制(通常 \(\le 10000\)),我们可以用双重循环枚举所有不同的数对 \((i, j)\)(\(i \neq j\)),计算它们的和,并用一个布尔数组
sum标记该和“可由两数之和得到”。最后,再遍历集合中的每个数,若sum[数]为真,则答案加 1。 - 时间复杂度:\(O(n^2 + n)\),非常适合 \(n\) 较小的情况。
- 标记数组 + 枚举:由于集合元素数量 \(n \le 100\),且每个数的大小有范围限制(通常 \(\le 10000\)),我们可以用双重循环枚举所有不同的数对 \((i, j)\)(\(i \neq j\)),计算它们的和,并用一个布尔数组
-
关键步骤:
- 读入与存储:读取 \(n\) 和 \(n\) 个正整数,存入数组
a[1..n]。 - 标记所有两数之和:使用双重循环枚举所有 \(i \neq j\) 的数对,计算
a[i] + a[j],将sum[和]标记为1(表示该和可由两个不同数相加得到)。 - 统计答案:遍历数组中的每个数
a[i],若sum[a[i]] == 1,则ans++。 - 输出:输出
ans。
- 读入与存储:读取 \(n\) 和 \(n\) 个正整数,存入数组
-
时间/空间复杂度:
- 时间复杂度:\(O(n^2)\),其中 \(n \le 100\),非常高效。
- 空间复杂度:\(O(\text{最大值})\),标记数组大小需能容纳两数之和的最大值。
-
模拟与集合运算:
- 去重机制:使用布尔数组标记和,而不是直接计数,可以确保即使一个和由多种数对组合得到,也只会计入答案一次。
- “另外两个数”:条件要求两个加数必须是集合中不同的数,因此枚举时需跳过
i == j的情况。 - 数据范围:由于题目标签为普及-,且 \(n\) 很小,直接枚举是最简洁且可靠的做法。
- 适用场景:这种“判断元素是否可由其他两个元素组合得到”的问题,当数据规模较小时,枚举所有组合是最直接的解决方案。
【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, ans;
int a[105], sum[20005];
int main()
{
cin >> n; // 输入n
for (int i=1; i<=n; i++) { // 输入n个数
cin >> a[i];
}
for (int i=1; i<=n; i++) { // 双重for循环枚举2个数
for (int j=1; j<=n; j++) {
if (i==j) continue; // 不同的2个数
sum[a[i]+a[j]] = 1; // 计算2个数的和,并在桶数组sum中置为1
}
}
for (int i=1; i<=n; i++) { // 枚举n个数
if (sum[a[i]]) ans++; // 如果桶数组中对应的值为1,说明是由2个数之和构成
}
cout << ans << endl;
return 0;
}
【运行结果】
4
1 2 3 4
2
浙公网安备 33010602011771号