题目描述:
ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
输入格式:
第一行:n(代表数列中数字的个数) (1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)
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
思路:
若对数据和一一枚举,则会到达n4的复杂度,明显会超时,因此我们采用首先使用前两个数组进行枚举,记录他们的和,然后再枚举后两个数组的和,同时看前两个数组中他们的相反数有多少个,并计入答案。这样复杂度可降到n2。在计算相反数有多少个时,曾采用map的记法,但仍然超时了,因此我们选择对前两个数组的和数组排序,然后进行二分,找出要找的数的第一个和最后一个位置,从而求出目标数据(后两个数组和的相反数)的个数。
代码:
#include <iostream>
#include<map>
#include<vector>
#include<algorithm>
#include<string.h>
using namespace std;
const int size=4000+10;
int d[4][size];
int sum[size*size];
int begin(int x,int left,int right)
{
int ans=-1;
while(left<=right)
{
int mid=(left+right)>>1;
if(sum[mid]==x)
{
ans=mid;
right=mid-1;
}
else if(sum[mid]<x)
left=mid+1;
else
right=mid-1;
}
return ans;
}
int end(int x,int left,int right)
{
int ans=-1;
while(left<=right)
{
int mid=(left+right)>>1;
if(sum[mid]==x)
{
ans=mid;
left=mid+1;
}
else if(sum[mid]<x)
left=mid+1;
else
right=mid-1;
}
return ans;
}
int main(int argc, char** argv) {
int n;
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%d%d%d%d",&d[0][i],&d[1][i],&d[2][i],&d[3][i]);
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
sum[i*n+j]=d[0][i]+d[1][j];
}
}
sort(sum,sum+n*n);
int count=0;
int l,r;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
int temp=d[2][i]+d[3][j];
temp=0-temp;
l=begin(temp,0,n*n-1);
if(l==-1)
continue;
r=end(temp,0,n*n-1);
count+=(r-l+1);
}
}
cout<<count<<endl;
return 0;
}