题目相关
题目链接
一本通 OJ,http://ybt.ssoier.cn:8088/problem_show.php?pid=1244。
计蒜客 OJ,https://nanti.jisuanke.com/t/T1158。
我的 OJ,http://47.110.135.197/problem.php?id=4248。
题目描述
给出若干个整数,询问其中是否有一对数的和等于给定的数。
输入
共三行:
第一行是整数 n,表示有 n 个整数。
第二行是 n 个整数。整数的范围是在 0 到 2×10^8 之间。
第三行是一个整数 m,表示需要得到的和。
输出
若存在和为 m 的数对,输出两个整数,小的在前,大的在后,中间用单个空格隔开。若有多个数对满足条件,选择数对中较小的数更小的。若找不到符合要求的数对,输出一行"No"
。
样例输入
4
2 5 1 4
6
样例输出
1 5
数据范围
0 < n ≤ 100,000
0 ≤ m ≤ 2^30
题目分析
题意
在一个数列中找,是否存在两个数,这两个数的和是 m。若有多个数对满足条件,选择数对中较小的数更小的。若找不到符合要求的数对,输出一行"No"
。
数据范围分析
n 的最大值是 100,000,也就是说,我们设计的算法时间复杂度不能超过 O(nlogn),否则就是 TLE。
数列中最大的数可能是 2×10^8,也就是说可以用 int 来表示。m 的最大值是 2^30,也就是说需要用 unsigned long long 来表示。因此本题的数据统一用 unsigned long long 表示。
样例数据分析
根据题目,输入的数列为 [2 5 1 4],m 为 6。我们知道数列中的 2+4=6,5+1=6。根据要求,输出较小的数,所以输出 1 5。
算法设计
由于算法的时间复杂度不能超过 O(nlogn),暴力肯定是 TLE 的。同时要求输出较小的数,那么我们进行一下排序,将较小的数放在前面,这样满足条件的第一个答案,自然是较小的数。
1、读入所有的数据,并保存。
2、排序数组。
3、遍历数组每一个元素 a[i],检查 a[i] 是否是答案。如果 m 大于等于 a[i],同时 m-a[i] 存在于本数组中,那么数对 a[i] 和 m-a[i] 就是答案。如果 m 小于 a[i],说明没有答案,输出 No,因为数组里的数取值范围为 [0, 2*10^8]。
为了降低时间复杂度,查找 m-a[i] 是否在数组中,我们使用二分查找。这样,这个算法的 f(n)=n*logn+n*logn,对应的时间复杂度为 O(nlogn),满足我们的需求。
AC 参考代码
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int MAXN = 1e5+6;
ULL a[MAXN] = {};
int n;
int main() {
//读入数据
scanf("%d", &n);
for (int i=0; i<n; i++) {
scanf("%lld", &a[i]);
}
ULL m;
scanf("%lld", &m);
//排序
sort(a, a+n);
//数据处理
for (int i=0; i<n; i++) {
if (m>=a[i]) {
//注意:a[i]可能为0
if (binary_search(a, a+n, m-a[i])) {
printf("%lld %lld\n", a[i], m-a[i]);
return 0;
}
} else {
break;
}
}
printf("No\n");
return 0;
}