UVa 1152 4 Values whose Sum is 0

本文介绍了一种解决四列表中求和等于零的元素组合数量的算法,包括输入输出格式、实现步骤及样例解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The SUM problem can be formulated as follows: given four lists ABCD of integer values, compute how many quadruplet (abcd ) $ \in$ 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 ABC 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). 通过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值