四数之和为零 [UVA – 1152] 简析
给四个长度相同的序列,从每个序列中选出一个数字组成一个四元组,有多少个四元组之和为0.
枚举是不可能的,O(N^4)的时间不可接受。两个序列的情况,对于序列a的每个数字,我们可以以总O(NlogN)的时间找到序列b中的数字与a中的那个之和为0,即对a中的x,用二分搜确定-x是否在b中(重复的都要记)。如果是三个序列,可以先将两个序列两两相加,组成一个长为N*N的序列,再对于第三个序列N,在这N*N的序列中进行二分搜,可以达O(N*log(N*N))=O(N*log(N))的时间。
而此题四个序列,若类似思路将三个序列相加的话将达N*N*N的空间,可能会超限,所以可以用两个N*N的序列,这样就问题时间O(N*N*logN),题目的N为4000,O(N^2*logN)还是可以在时限内的
#include <cstdio> #include <cstdlib> #include <algorithm> using std::sort; using std::binary_search; using std::lower_bound; using std::upper_bound; int binsearchCount(int x,int *a,int N) { int count=0; int *lwbd=a,*upbd=a+N; if( binary_search(a,a+N,x) ) { count+=upper_bound(a,a+N,x)-lower_bound(a,a+N,x); } return count; } int main(void) { int *a,*b,*c,*d; int i,j,N,count,allN; int *sab; scanf("%d",&allN); for(int idx=0;idx<allN;idx++) { scanf("%d",&N); a=new int [N]; b=new int [N]; c=new int [N]; d=new int [N]; for(i=0;i<N;i++) { scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]); } sab=new int [N*N]; for(i=0;i<N;i++) { for(j=0;j<N;j++) sab[i*N+j]=a[i]+b[j]; } sort(sab,sab+N*N); delete [] a; delete [] b; count=0; for(i=0;i<N;i++) { for(j=0;j<N;j++) { count+=binsearchCount(-c[i]-d[j],sab,N*N); } } delete [] c; delete [] d; if(idx>0) printf("\n"); printf("%d\n",count); delete [] sab; } return 0; }