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 input begins with a single positive integer on a line by itself indicating the number of the cases following, each of them as described below. This line is followed by a blank line, and there is also a blank line between two consecutive inputs.
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 228 )
that belong respectively to A, B, C and D .
Output
For each test case, the output must follow the description below. The outputs of two consecutive cases will be separated by a blank line.
For each input file, your program has to write the number quadruplets whose sum is zero.
Sample Input
1 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
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).
#include <cstdio>
#include <algorithm>
using namespace std;
long long array[4][4100];
int array_length;
//int count_sum(long long x, int a, int b);
long long count_sum(long long x, long long* a, long long* b, int len1, int len2);
long long sum_array[2][16000100];
int main()
{
int n;
scanf("%d", &n);
int count = 0;
while(count < n)
{
scanf("%d", &array_length);
// 读入所有数据
for(int i = 0; i < array_length; i++)
{
for(int j = 0; j < 4; j++)
scanf("%lld", &array[j][i]);
}
// 将所有数据排序
for(int i = 0; i < 4; i++)
sort(array[i], array[i]+array_length);
/* // 遍历前两个数组的所有情况,得到的和再用count_sum计算
long long zero_count = 0;
for(int i = 0; i < array_length; i++)
{
if(i-1 >= 0 && array[0][i] == array[0][i-1])
continue;
for(int j = 0; j < array_length; j++)
{
if(j-1 >= 0 && array[1][j] == array[1][j-1])
continue;
zero_count = zero_count + count_sum(-(array[0][i]+array[1][j]), 2,3);
}
}
*/
// 计算数组1,2和数组3,4的和
int sum_length1 = 0;
int sum_length2 = 0;
for(int i = 0; i < array_length; i++)
{
/* int flag1 = 0;
int flag2 = 0;
if(i-1 >= 0 && array[0][i] == array[0][i-1])
flag1 = 1;
if(i-1 >= 0 && array[2][i] == array[2][i-1])
flag2 = 1;
if(flag1 && flag2)
continue;
*/ for(int j = 0; j < array_length; j++)
{
/* if(!(j-1 >= 0 && array[1][j] == array[1][j-1]))
{
*/ sum_array[0][sum_length1] = array[0][i]+array[1][j];
sum_length1++;
// }
/* if(!(j-1 >= 0 && array[3][j] == array[3][j-1]))
{
*/ sum_array[1][sum_length2] = array[2][i]+array[3][j];
sum_length2++;
// }
}
}
sort(sum_array[0], sum_array[0]+sum_length1);
sort(sum_array[1], sum_array[1]+sum_length2);
/* printf("sum1: ");
for(int i = 0; i < sum_length1; i++)
printf("%lld ", sum_array[0][i]);
printf("\nsum2: ");
for(int i = 0; i < sum_length2; i++)
printf("%lld ", sum_array[1][i]);
printf("\n");
*/ long long zero_count = count_sum(0, sum_array[0], sum_array[1], sum_length1, sum_length2);
if(count > 0)
printf("\n");
count++;
printf("%lld\n", zero_count);
}
return 0;
}
// 统计两个第a,b数组中元素和为x的元素对个数
// 从a数组的开头和b数组的结尾开始扫描
long long count_sum(long long x, long long* a, long long* b, int len1, int len2)
{
int begin1 = 0;
int begin2 = len2-1;
long long count = 0;
int change1 = 0, change2 = 0;
while(begin1 <= len1-1 && begin2 >= 0)
{
long long now = a[begin1] + b[begin2];
change1 = 0;
change2 = 0;
if(now == x)
{
// count++;
int count1 = 1;
int count2 = 1;
long long pre1 = a[begin1];
long long pre2 = b[begin2];
begin1++;
begin2--;
while(begin1 <= len1-1 && a[begin1] == pre1)
{
count1++;
begin1++;
}
while(begin2 >= 0 && b[begin2] == pre2)
{
count2++;
begin2--;
}
count = count + count1*count2;
}
else if(now < x)
{
change1 = 1;
}
else
{
change2 = 1;
}
if(change1 == 1)
{
long long pre = a[begin1];
begin1++;
/* while(begin1 <= len1-1 && a[begin1] == pre)
begin1++;
*/ }
if(change2 == 1)
{
long long pre = b[begin2];
begin2--;
/* while(begin2 >= 0 && b[begin2] == pre)
begin2--;
*/ }
}
return count;
}
这题一开始想到了一个问题:两个有序的序列a,b. 如果要求有多少个a,b中元素的对能够得到指定的和(比如x),是可以从a的开头,b的结尾扫描一遍确定,为O(n). (a,b长度为n). 即count_sum函数
一开始想到的方法是:先将a,b,c,d排序。遍历出a,b的所有元素和,设为i+j, 针对每个和i+j, 用上述方法来查找c,d中有多少个元素对的和为-(i+j). 时间复杂度为O(n^3). 超时。
后来沿着这个思路找到一个改进的方法:遍历出a,b的所有元素和,设该序列为e. 遍历出c,d的所有元素和,设该序列为f. 将e,f排序。然后对e,f查找有多少个元素对的和为0. 时间复杂度为O(n^2*logn). 通过。