4 Values whose Sum is 0
Time Limit: 15000MS | Memory Limit: 228000K | |
Total Submissions: 21314 | Accepted: 6410 | |
Case Time Limit: 5000MS |
Description
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).
大致题意:
给出四列数,从每一列中选一个数,使这四个数之和等于零。求问一共有多少中选法。
大体思路:
1.二分。
1)计算前两列任意两个数之和(总共n*n个),并从小到大排序。
2)计算后两列任意两个数之和。对于每一个和,用二分法在前一个数组中搜索它的相反数。ans += 。
2.哈希。
1)计算前两列任意两书之和,用哈希表保存。
2)计算后两列任意两数之和,对每一个和,搜索哈希表中它的相反数的个数。ans+=。
哈希比二分快很多。。。
哈希代码:
#include<cstdio>
#include<cstring>
#define limit 4001
/*******创建哈希表时被模的那个数
应是一个足够大的质数*******/
#define M 21331131
#define rep(i,a,b) for(int i=a; i<=b; i++)
#define mem(a,b) memset(a,b,sizeof(a))
const int inf = (1<<29);
struct node
{
node(){}
/*******三个参数:v 值, pre 同槽中的前一个元素, cnt 元素出现的次数*******/
node(int v, int p, int c){val = v, pre = p, cnt = c;}
int val, pre, cnt;
};
int a[limit], b[limit], c[limit], d[limit];//保存四列数
int n;
/*******head 是哈希表的头指针表, top是哈希表已填充段的最右端+1,即下一个填充位置*******/
int head [M], top;
/*******哈希表*******/
node hs [limit * limit];
/*******哈希表的插入函数*******/
void insert (int v)
{
int pos = ( v + inf ) % M;//确定该元素所在的槽
for(int i= head[pos]; i!= -1; i= hs[i].pre )//遍历该槽中的元素
if(v == hs[i].val){//发现该值已存在于槽中
hs[i].cnt++;//个数加1即可
return;
}
hs[top] = node(v, head[pos], 1);//槽中还没有这个值,新建一个点,插入对应的槽中
//v就是值,pre就是当前头指针表中该槽的值,次数初始化为1
head[pos] = top ++; //更新头指针表,该槽的第一个变成top。之后top自增,保持左闭右开
}
/********哈希表的查找函数*******/
int find (int v)
{
int pos = ( v + inf ) % M;
for(int i= head[pos]; i!= -1; i= hs[i].pre)
if(v == hs[i].val)//找到该值,返回其出现的次数
return hs[i].cnt;
return 0;//没找到,返回0
}
int main ()
{
while( ~scanf("%d",&n) ){
/********头指针表初始化为-1*******/
mem(head,-1);
rep(i,1,n)//输入四列数字
scanf("%d%d%d%d", a+i, b+i, c+i, d+i);
rep(i,1,n)//把前两列产生的和全部插入哈希表
rep(j,1,n)
insert(a[i]+b[j]);
long long ans = 0;//答案初始化为0
rep(i,1,n)
rep(j,1,n){
int t = c[i] + d[j];
ans += find(-t);//查找t的相反数出现的次数
}
printf("%lld\n",ans);
}
return 0;
}
二分代码:
#include<cstdio>
#include<algorithm>
#define _max 4010
long long A [_max],B [_max],C[_max],D[_max];
long long E [_max*_max],F [_max*_max];
int n;
long long ans=0;
int upper (int x,int k)
{
int l=0,r=k-1;
while(l<=r){
int mid=(l+r)>>1;
if(E[mid]+x<=0) l=mid+1;
else r=mid-1;
}
return x+E[l-1]==0?l-1:0;
}
int lower (int x,int k)
{
int l=0,r=k-1;
while(l<=r){
int mid=(l+r)>>1;
if(E[mid]+x>=0) r=mid-1;
else l=mid+1;
}
return x+E[l]==0?l-1:0;
}
int main ()
{
//freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld%lld%lld%lld",A+i,B+i,C+i,D+i);
int i=0,k=0,j;
for(;i<n;i++)
for(j=0;j<n;j++)
E[k]=A[i]+B[j],F[k]=C[i]+D[j],k++;
std::sort(E,E+k);
for(int i=0;i<k;i++)
ans+=upper(F[i],k)-lower(F[i],k);
printf("%I64d\n",ans);
}