题目描述
ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!
输入
输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。 每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。 然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。 |
输出
输出不同组合的个数。 |
样例输入
样例输出
思路
综述
这道题是一道整数二分算法题;
题干要求:a+b+c+d=0
如果四层循环O(n^4)在数据量是4000的情况下太高,不合适;
变换:a+b = -c-d;
只需要两遍O(n^2)复杂度循环解决
过程
step1:输入数据
注意:scanf会缩短时间
for (i = 0; i < n; i++) {
//cin >> a[i] >> b[i] >> c[i] >> d[i];
scanf("%lld %lld %lld %lld", &a[i], &b[i], &c[i], &d[i]);
}
step2:
计算-c-d并排序,以便用于二分;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
num2[jishi] = -c[i] - d[j];
jishi++;
}
}
sort(num2, num2 + (n * n),cmp);
step3:
计数:
每计算一个a+b 便二分搜索,找到排好序的数组中第一个等于a+b的序号和最后一个a+b的序号,两者相减+1便是结果;
循环上述,并且累加;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
num1[jishi] = a[i] + b[j];
seq = findmax(num1[jishi]);
if (seq != -1)
tot += seq - findmin(num1[jishi]) + 1;
jishi++;
}
}
总结
共发现4个问题
问题1:(用于记录最大结果数的tot)
这个题,用于存储结果的值,更合理的是用长整型,long long,虽然int也可以过,但是如果出现6个数组都是4000个0那么就会出错
问题2:find函数的问题
在下代码处,加注释“记录”的地方,需要特别注意,应该是length-1;(当然其他写法如while(l<r)可有其他处理方式)
int findmin(long long x){
int l=0;
int r = n * n-1; //记录!!!!
int ans = -1;
int mid;
while (l <= r) {
mid = (l + r) >> 1;
if (num2[mid] == x) {
ans = mid;
r = mid - 1;
}
else if (num2[mid] < x) l = mid + 1;
else if (num2[mid] > x) r = mid - 1;
}
return ans;
}
问题3:scanf和cin问题
左侧是使用cin的耗时,右侧是使用scanf的耗时
问题4:计数器的问题
在如下的代码中,jishi这个int型变量,可以用"n*i+j"来代替,但是外层双循环,复杂度高,用jishi来代替可以一定程度优化。
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
num1[jishi] = a[i] + b[j];
seq = findmax(num1[jishi]);
if (seq != -1)
tot += seq - findmin(num1[jishi]) + 1;
jishi++;
}
}
代码
//cmp超时问题
//find函数问题
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
long long n;
int tot = 0;
long long a[5000], b[5000], c[5000], d[5000];
long long num1[16000050], num2[16000050];
bool cmp(long long &a,long long &b) {
return a < b;//小到大
}
//小到大
int findmin(long long x){
int l=0;
long long r = n * n-1; //记录!!!!
int ans = -1;
int mid;
while (l <= r) {
mid = (l + r) >> 1;
if (num2[mid] == x) {
ans = mid;
r = mid - 1;
}
else if (num2[mid] < x) l = mid + 1;
else if (num2[mid] > x) r = mid - 1;
}
return ans;
}
int findmax(long long x) {
int l=0;
long long r = n * n-1;
int ans = -1;
int mid;
while (l <= r) {
mid = (l + r) >> 1;
if (num2[mid] == x) {
ans = mid;
l = mid + 1;
}
else if (num2[mid] < x) l = mid + 1;
else if (num2[mid] > x) r = mid - 1;
}
return ans;
}
int main() {
long long i,j;
cin >> n;
for (i = 0; i < n; i++) {
//cin >> a[i] >> b[i]//cmp超时问题
//find函数问题
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
long long n;
int tot = 0;
int a[5000], b[5000], c[5000], d[5000];
int num1[16000050], num2[16000050];
bool cmp(long long &a,long long &b) {
return a < b;//小到大
}
//小到大
int findmin(long long x){
int l=0;
int r = n * n-1; //记录!!!!
int ans = -1;
int mid;
while (l <= r) {
mid = (l + r) >> 1;
if (num2[mid] == x) {
ans = mid;
r = mid - 1;
}
else if (num2[mid] < x) l = mid + 1;
else if (num2[mid] > x) r = mid - 1;
}
return ans;
}
int findmax(long long x) {
int l=0;
long long r = n * n-1;
int ans = -1;
int mid;
while (l <= r) {
mid = (l + r) >> 1;
if (num2[mid] == x) {
ans = mid;
l = mid + 1;
}
else if (num2[mid] < x) l = mid + 1;
else if (num2[mid] > x) r = mid - 1;
}
return ans;
}
int main() {
long long i,j;
cin >> n;
for (i = 0; i < n; i++) {
//cin >> a[i] >> b[i] >> c[i] >> d[i];
scanf("%lld %lld %lld %lld", &a[i], &b[i], &c[i], &d[i]);
}
int jishi=0;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
num2[jishi] = -c[i] - d[j];
jishi++;
}
}
sort(num2, num2 + (n * n),cmp);
int seq;
jishi=0;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
num1[jishi] = a[i] + b[j];
seq = findmax(num1[jishi]);
if (seq != -1)
tot += seq - findmin(num1[jishi]) + 1;
jishi++;
}
}
cout << tot << endl;
}