题意:
给定 n 行数,每行都有 4 个数A,B,C,D。
要从每列中各抽取出一个数,问使四个数的和为0的所有方案数。
相同数字不同位置当作不同数字对待。
题解:
如果采用暴力的话,从4个数列中选择数组合,共有(N^4)种选择,故时间复杂度为O(N^4),指定会超时。
但,如果将它们分成 AB,CD两组,每组只有 N^2 个组合,而 N 的数据范围为 N < 4000,故采用此种方法不会超时。
AC代码:
1 #include2 #include 3 #include 4 using namespace std; 5 #define ll long long 6 const int maxn=4000+50; 7 8 int n; 9 int a[maxn*maxn];10 int b[maxn*maxn];11 int num[maxn][5];12 13 ll Solve()14 {15 int index=1;16 for(int i=1;i <= n;++i)17 for(int j=1;j <= n;++j)18 {19 a[index]=num[i][1]+num[j][2];//存储所有的A+B的值20 b[index]=num[i][3]+num[j][4];//存储所有的C+D的值21 index++;22 }23 sort(a+1,a+index);24 sort(b+1,b+index);25 ll res=0;26 /**27 A+B+C+D=0 -> C+D=-(A+B)28 而C+D的所有值已经预处理好,故可通过二分查找存在于b[]中 -(A+B) 的个数即可29 **/30 for(int i=1;i < index;++i)31 {32 int t=lower_bound(b+1,b+index,-a[i])-b;33 int k=0;34 while(t < index && b[t] == -a[i])//查找 -(A+B) 的个数35 k++,t++;36 res += k;37 }38 return res;39 }40 41 int main()42 {43 while(~scanf("%d",&n))44 {45 for(int i=1;i <= n;++i)46 for(int j=1;j <= 4;++j)47 scanf("%d",&num[i][j]);48 printf("%lld\n",Solve());49 }50 return 0;51 }