4 Values whose Sum is 0
Time Limit: 15000MS | Memory Limit: 228000K | |
Total Submissions: 16375 | Accepted: 4748 | |
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).
Source
题意:本题题意很简单,给定4组数,每组有n个数,从每组数中各取一个数,问有多少种取法使得取出的四个数之和为0
思路:很容易想到用暴力枚举的方法,时间复杂度为O(n^4),必然超时,而对于这种从集合中选组合类的问题,我们通常采用一种方法,即
中途相遇法
中途相遇法:就是从两个方向向中间靠拢的一种做法,如双向BFS,对于此题,我们可以把前两组的组合和后两组的组合分开计算,然后再看是否之和为0,即我们先计算前两组数的和,利用数据结构存储起来,再计算后两组数的和,进行查询,这样时间复杂度就降低到O(n^2*查询的时间复杂度),由于本题数据范围较大,无法使用vis数组进行O(1)的查询,因此只能考虑使用map或者hash表
由于map的代码复杂度较低,时间复杂度为O(n^2*logn),我们可以先尝试用map写一个demo程序
使用map的代码如下
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
map<int,int>MAP;
int a[5][5000];
int main()
{
int n,i,j,tt,ans;
while(~scanf("%d",&n))
{
ans=0;
MAP.clear();
for(j=1;j<=n;j++)
for(i=1;i<=4;i++)
scanf("%d",&a[i][j]);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
tt=a[1][i]+a[2][j];
MAP[tt]++;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
tt=a[3][i]+a[4][j];
ans+=MAP[-tt];
}
printf("%d\n",ans);
}
return 0;
}
很遗憾,使用map会超时,于是我们考虑使用hash,实现hash的方法主要使用开放寻址法或者哈希邻接表
利用head[]数组和next[]数组可以轻松完成哈希邻接表,我们使用除法取余作为哈希函数,head数组的大小为素数mod的大小,
这里素数取得越大,发生碰撞的可能性就越小,next[]的大小为哈希表中一共需要存入数据个数的大小,即前两组数的组合个数,n^2
哈希AC代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
//const int mod=3999971;
const int mod=10000007;
int head[mod+50];
int next[16000000+50];
int a[5][5000];
int res[16000000+50];
void insert_hash(int x)
{
int v=abs(res[x]%mod);
next[x]=head[v];
head[v]=x;
}
int find_hash(int x)
{
int cnt=0;
int t=abs(x);
int u=head[t%mod];
while(u!=-1)
{
if(res[u]==x)
cnt++;
u=next[u];
}
return cnt;
}
int main()
{
int n,i,j,tt,ans,k;
while(~scanf("%d",&n))
{
ans=0;
memset(head,-1,sizeof head);
for(j=1;j<=n;j++)
for(i=1;i<=4;i++)
scanf("%d",&a[i][j]);
k=0;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
tt=a[1][i]+a[2][j];
res[++k]=tt;
insert_hash(k);
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
tt=a[3][i]+a[4][j];
ans+=find_hash(-tt);
}
printf("%d\n",ans);
}
return 0;
}