题意
ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!
Input
第一行:n(代表数列中数字的个数) (1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)
Output
输出不同组合的个数。
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
样例解释: (-45, -27, 42, 30),
(26, 30, -10, -46), (-32, 22, 56, -46)
(-32,30, -75, 77), (-32, -54, 56, 30).
思路
如果用四重循环暴力查找,很明显n⁴复杂度,我们没办法接受。
我们将四组数据先分成两组,即将a+b的所有组合做为一组数据ab,c+d的所有组合为一种数据cd;
然后再查找ab+cd=0的组合。查找过程可以采用二分思想,具体看代码。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int a[4005];
int b[4005];
int c[4005];
int d[4005];
int ab[16000010]; //a+b 4000个a+4000个b 最多组合16000000个
int cd[16000010]; //c+d
int ans=0;
int main(int argc, char** argv)
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d %d %d %d",&a[i],&b[i],&c[i],&d[i]);
}
int len=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
ab[len]=a[i]+b[j];
cd[len]=c[i]+d[j];
len++;
}
} //二重循环 计算a+b c+d
sort(ab,ab+len); //将ab排序 才能使用二分 降低查找复杂度到log
int l,r,mid;
for(int i=0;i<len;i++)
{//枚举cd[i] 查找复合条件的第一个ab[i]
l=0,r=len-1;
while(l<r)
{//二分过程
mid=(l+r)>>1;
if(cd[i]+ab[mid]<0)
{
l=mid+1;
}
else
{
r=mid;
}
}
//cout<<ab[l]<<endl;
while(l!=len&&cd[i]+ab[l]==0)
{//如果符合条件的不只之一 因为ab排过序 向后查找即可
ans++;
l++;
}
}
cout<<ans;
return 0;
}
总结
先分组相当于优化一下数据,然后用二分方法查找,降低复杂度。