题目:四个数列
题意:ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!
输入:
第一行:n(代表数列中数字的个数) (1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)
输出:
输出不同组合的个数。
样例:
解题思路:首先想想暴力,O(n4)直接暴力,然后超时(手动滑稽)。然后优化,我们可以这样,先将A,B的和算出来然后弄成一个数组并排序,这样时间复杂度是O(n2logn2)。然后再求出来C+D,之后进行二分还是O(n2logn2),找C+D的相反数在A+B中的位置,如果有就算一个结果,这样利用二分时间复杂度是n2*logn的,是可以接受的。
代码:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int a[4][4005];
vector<int> h,z;
int main()
{
int n,x;
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<4;j++)
{
cin>>x;
a[j][i]=x;
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
h.push_back(a[0][i]+a[1][j]);
}
}
sort(h.begin(),h.end());//A+B排序
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
z.push_back(a[2][i]+a[3][j]);
}
}//C+D不用排序
int total=0;
for(int i=0;i<z.size();i++)
{
z[i]=-z[i];
int l=0,r=h.size()-1;
int ans=-1;
while(l<=r)//找相反数二分
{
int mid=(l+r)/2;
if(h[mid]>z[i])
{
r=mid-1;
}else if(h[mid]<z[i])
{
l=mid+1;
}else if(h[mid]==z[i])
{
ans=mid;
l=mid+1;
}
}
if(ans==-1)//没找到,白给
{
continue;
}
if(h[ans]==z[i])
{
total++;//找到
for(int j=ans-1;j>=0;j--)//在这个数周围找找看有没有相同的
{
if(h[j]==z[i])
{
total++;
}else
{
break;
}
}
for(int j=ans+1;j<h.size();j++)
{
if(h[j]==z[i])
{
total++;
}else
{
break;
}
}
}
}
cout<<total<<endl;
}