题意
给定n行4列的整数,从每一列各选择一个数加起来等于0的组合有多少种?
思路
这题很容易让人想到枚举,要让四个数字的和等于0,从前三列选择了三个数以后第四列要找的数就是确定的,可以不断地枚举找到答案,但这样时间复杂度为,这可不是我们负担的起的。
所以在枚举的基础上可以加入二分,确定了前三列的数字以后,对第四列进行排序,之后再从第四列种二分查找判断有多少个我们要找的数即可。
但即使是这样,我们对四列数据的枚举仍会带来大量重复的计算,所以我们可以对第一、二列的数据两两求和,形成一个新的数组,对第三、四列进行一样的操作,这样我们就只剩下两个数组,每个数组的长度为。
之后再对第二个数组排序,然后对第一个数组进行枚举,再从第二个数组里二分来统计我们要找的数的个数,把答案相加即可。
代码
*因为正在学二分,所以我把代码里的lowerbound和upperbound自己写了一下,没拿出来验证是不是对的,但是能AC
#include <iostream> #include <algorithm> using namespace std; int n; //数据的行数 int num[4005][4]; int part1[16000025]; //第一列和第二列的和 int part2[16000025]; //第三列和第四列的和 //返回x在part2中第一次出现的下标 //相当于algorithm中的lower_bound int findLow(int x) { int left = 0, right = n * n - 1, mid; while (left <= right) { mid = (left + right) >> 1; if (part2[mid] < x)left = mid + 1; else right = mid - 1; } return left; } //返回x在part2中最后一次出现的下标的下一个 //相当于algorithm中的upper_bound int findUp(int x) { int left = 0, right = n * n - 1, mid; while (left <= right) { mid = (left + right) >> 1; if (part2[mid] <= x)left = mid + 1; else right = mid - 1; } return left; } int main() { cin >> n; for (int i = 0; i < n; ++i) { for (int j = 0; j < 4; ++j) { cin >> num[i][j]; } } //把第一、二列和第三、四列的数两两相加 for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { part1[(i - 1) * n + j - 1] = num[i - 1][0] + num[j - 1][1]; part2[(i - 1) * n + j - 1] = num[i - 1][2] + num[j - 1][3]; } } sort(part2, part2 + n * n); int res = 0; //记录答案 //对第一列枚举,从第二列中二分统计能让和为0的数的个数 for (int i = 0; i < n * n; ++i) { res += findUp(-part1[i]) - findLow(-part1[i]); } cout << res; }