poj 2785(折半枚举+二分搜索)

传送门:Problem 2785

 

题意:

  给定 n 行数,每行都有 4 个数A,B,C,D。

  要从每列中各抽取出一个数,问使四个数的和为0的所有方案数。

  相同数字不同位置当作不同数字对待。

题解:

  如果采用暴力的话,从4个数列中选择数组合,共有(N^4)种选择,故时间复杂度为O(N^4),指定会超时。

  但,如果将它们分成 AB,CD两组,每组只有 N^2 个组合,而 N 的数据范围为 N < 4000,故采用此种方法不会超时。

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 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 }
View Code

 

posted @ 2018-10-17 18:44  HHHyacinth  阅读(139)  评论(0编辑  收藏  举报