先讲述一下题意:有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;
}