The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, compute how many quadruplet (a, b, c, d ) ∈ A x B x C x D are such that a + b + c + d = 0 . In the following, we assume that all lists have the same size n .
Input
The first line of the input file contains the size of the lists n (this value can be as large as 4000). We then have n lines containing four integer values (with absolute value as large as 2 28 ) that belong respectively to A, B, C and D .
Output
For each input file, your program has to write the number quadruplets whose sum is zero.
Sample Input
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45
Sample Output
5
Hint
Sample Explanation: Indeed, the sum of the five following quadruplets is zero: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).
思路
将A+B数组的和存入一个新数组sum1,C+D数组的和的相反数(这样sum2的值在sum1找到了的话即A+B+C+D的值为0)存入一个新数组sum2。将sum1数组排序以便二分,C+D数组在sum1数组中查找第一次出现的位置和最后一次出现的位置,相减加一即得满足该值的情况数,最后汇总即是结果。
代码:
#include<iostream>
using namespace std;
#include<map>
#include<stack>
#include<set>
#include<queue>
#include<cstring>
#include<sstream>
#include<cmath>
#include<limits.h>
#include<algorithm>
#include<cstdio>
#define ll long long
const int N=20000000;
int n;
int A[4010];
int B[4010];
int C[4010];
int D[4010];
int sum1[N];
int sum2[N];
int k=1;
//计算元素第一次出现的位置
int getStart(int value){
int low=1,high=k-1;
while(low<=high){
int mid=low+((high-low)>>1);
if(sum1[mid]<value){
low=mid+1;
}else if(sum1[mid]>value){
high=mid-1;
}else{
if(mid==1||(sum1[mid-1]!=value)){ //如果我们查找到了等于value的下标,判断它下标是否为1或者它前一个位置是否等于value
return mid;
}else{
high=mid-1;//没找到,舍弃这个等于value的下标,往前找。
}
}
}
return -1;
}
//计算元素最后一次出现的位置 ,与getStart基本相同
int getEnd(int value){
int low=1,high=k-1;
while(low<=high){
int mid=low+((high-low)>>1);
if(sum1[mid]<value){
low=mid+1;
}else if(sum1[mid]>value){
high=mid-1;
}else{
if(mid==k-1||(sum1[mid+1]!=value)){
return mid;
}else{
low=mid+1;
}
}
}
return -1;
}
int main() {
int n;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&A[i],&B[i],&C[i],&D[i]);
}
//int k=1; 计算A+B数组的和
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
sum1[k++]=A[i]+B[j];
}
}
sort(sum1+1,sum1+k); //对A+B数组和进行排序以便二分
int m=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
sum2[m++]=(C[i]+D[j])*(-1);//乘以-1的原因是待会在sum1上如果查找到相同的元素则表明四个数的结果为0
}
}
int sum=0;//记录情况总数
for(int i=1;i<m;i++){
int start=getStart(sum2[i]); //考虑到有相同的元素,所以这里记录该元素的第一次出现的位置
int end=getEnd(sum2[i]); //最后一次出现的位置
if(start==-1){ //对于元素不存在的丢弃
continue;
}
sum+=end-start+1;
}
cout<<sum<<endl;
return 0;
}