UVA 1152 4 Values whose Sum is 0

本文介绍了一种解决四数之和问题的有效算法。通过预处理将两数之和存储,再利用二分查找匹配剩余两数之和,显著提高了求解效率。

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

先讲述一下题意:有N组数据,每组数据第一行输入一个整数n,代表接下来有n行的输入,接下来输入n行,每行四个数A,B,C,D。从这n个A,B,C,D中各自挑选一个数,使得四个数的和为0,求满足条件的方案数。

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

比如这组样例,满足的条件是

 A    B   C   D
-45, -27, 42, 30
26, 30, -10, -46
-32, 22, 56, -46
-32, 30, -75, 77
-32, -54, 56, 30

一共四组,所以输出4。

如果暴力枚举的话,是4000^4,肯定会T掉,我们可以先求出-A与-B和的所有情况,存在数组num[]中,

		int t = 0;
		for(int i = 0; i < n; i++) {
			for(int j = 0; j < n; j++) {
				num[t++] = -s[i].A-s[j].B;//将所有A与B相加的可能存进数组
			}
		}
然后枚举C与D相加的所有情况,对num[]进行二分查找。

		int sum = 0;
		sort(num, num+t);    //排序 
		for(int i = 0; i < n; i++) {
			for(int j = 0; j < n; j++) {
				int val = s[i].C+s[j].D;
				sum += upper_bound(num, num+t, val)-lower_bound(num, num+t, val);
			}
		}
这里介绍一下两个二分查找函数:
lower_bound(num, num+t, val)-num:返回大于等于val的第一个元素位置(坐标)。如果所有元素都小于val, 则返回last的位置。
upper_bound(num, num+t, val)-num:返回大于val的第一个元素位置(坐标)。如果所有元素都小于val,则返回last的位置。

如果ans = 2; num[]数组为1 1 2 2 2 2 3 5 6,这种情况,那么lower_bound()返回的是2,而upper_bound()返回的是6,所以[2,6)区间所有的情况都可以满足,因为num[]数组中相同的元素代表不同的A与B的和,所以不能因为相同就合并,所以需要把所有的情况都写上。顾直接写成:sum += upper_bound(num, num+t, ans)-lower_bound(num, num+t, ans),更方便。

具体请看代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
struct node{
	int A, B, C, D;
}s[4005];
int num[16000005];
int main() {
	int N, n;
	scanf("%d", &N);
	while(N--) {
		scanf("%d", &n);
		for(int i = 0; i < n; i++) {
			scanf("%d %d %d %d", &s[i].A, &s[i].B, &s[i].C, &s[i].D);
		}
		int t = 0;
		for(int i = 0; i < n; i++) {
			for(int j = 0; j < n; j++) {
				num[t++] =-s[i].A-s[j].B;
			}
		}
		int sum = 0;
		sort(num, num+t);    //排序 
		for(int i = 0; i < n; i++) {
			for(int j = 0; j < n; j++) {
				int ans = s[i].C+s[j].D;
				sum += upper_bound(num, num+t, ans)-lower_bound(num, num+t, ans);
			}
		}
		printf("%d\n", sum);
		if(N) puts("");//格式问题
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值