题目描述:
小林拥有 \(2\) 个集合,亮亮拥有 \(3\) 个集合,这五个集合大小相等,且集合中包
含的都是整数。现在他们两个要进行心算比赛。比赛的规则是,将这五个集合放在一起,谁能先从每个集合中各选一个数,使得选出的五个数之和为 \(0\),谁就获得胜利。由于这五个集合都不小,而小林和亮亮事先并不知道是否能存在这样的五个数,因此他们决定先把五个集合都交给你,由你来编程判断是否存在符合条件的五个数。
输入格式:
第一行一个整数 \(N\) ,表示集合的大小。 接下来五行每行 \(N\) 个整数,表示这五个集合内的元素。
输出格式:
如果能找到符合条件的五个数,则输出“YES”,否则输出“NO”。
数据范围:
\(a[i] \le 1e8, n \le 200\)
思路:
这个题目如果用爆搜并且在输出“YES”之后就退出,这样大概能够拿七十几分,如果要AC的话必须要对这个爆搜进行优化。首先我们可以预处理出第一个集合与第二个集合之间任意一对数的和,之后我们再预处理出第四个集合与第五个集合之间任意一对数的和。之后我们先枚举第一个集合与第二个集合之间任意一对数的和,之后我们再枚举第三个集合的数。然后我们相当于只要在四个集合与第五个集合之间任意一对数的和中找到一个数等于第三个的数的负数,如果找到就输出“YES”,并停止程序,要不然就一直找。这样时间复杂度就被降成了 \(O(N^3logN)\) 。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5;
int n;
int arr_tmp[10][N];
int arr[10][N];
int tail1 = 0, tail2 = 0;
int read() {
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * w;
}
int main(int argc, char const *argv[]) {
//从文件输入输出
freopen("zero.in", "r", stdin);
freopen("zero.out", "w", stdout);
n = read();
for (register int i = 1; i <= 5; ++i)
for (register int j = 1; j <= n; ++j)
arr_tmp[i][j] = read();
for (register int i = 1; i <= n; ++i) {
for (register int j = 1; j <= n; ++j) {
arr[1][++tail1] = arr_tmp[1][i] + arr_tmp[2][j];
arr[2][++tail2] = arr_tmp[4][i] + arr_tmp[5][j];
}
}
sort(arr[1] + 1, arr[1] + 1 + tail1);
sort(arr[2] + 1, arr[2] + 1 + tail2);
bool flag = false;
for (register int i = 1; i <= n; ++i) {
for (register int j = 1; j <= tail2; ++j) {
int t = (-arr_tmp[3][i]) - arr[1][j];
if (binary_search(arr[2] + 1, arr[2] + 1 + tail2, t)) {
printf("YES\n"), flag = true;
break;
}
}
if (flag) break;
}
if (!flag) printf("NO\n");
return 0;
}