【uva 1152】4 Values Whose Sum is Zero(算法效率--中途相遇法+Hash或STL库)

题意:给定4个N元素几个A,B,C,D,要求分别从中选取一个元素a,b,c,d使得a+b+c+d=0。问有多少种选法。(N≤4000,D≤2^28)

解法:首先我们从最直接最暴力的方法开始思考:四重循环O(n^4)枚举;三重循环枚举,把剩下的一个集合排序后二分查找,O(n^3 log n)。在进一步想,运用“中途相遇法”:从两个不同的方向来解决问题,最后“汇集”到一起的方法。(有类似于“双向广度优先搜索”的思想)通过两重循环枚举出A,B两个集合中的元素可组成的和,一般想到开个以和为值的数组记录个数。但是由于D值有2^28这么大,所以不行。于是——解决技巧很厉害!(。ì _ í  。)

实现1:既然不能将相同的数一个个竖向累积在一个数组位,那就可以把这些相同的数横向拉伸,排成一排。使用STL库头文件algorithm里的upper_bound和lower_bound函数直接查找到>d和>=d的数的位置,相减就是d值的数的个数。这样的时间复杂度是O(n^2 log n)。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<iostream>
 6 using namespace std;
 7 
 8 const int N=4010;
 9 int a[N],b[N],c[N],d[N],sum[N*N];
10 
11 int main()
12 {
13     int T;
14     scanf("%d",&T);
15     while (T--)
16     {
17       int n;
18       scanf("%d",&n);
19       for (int i=1;i<=n;i++)
20         scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
21       int m=0;
22       for (int i=1;i<=n;i++)
23         for (int j=1;j<=n;j++)
24           sum[++m]=a[i]+b[j];
25       sort(sum+1,sum+1+m);
26       long long cnt=0;//long long
27       for (int i=1;i<=n;i++)
28         for (int j=1;j<=n;j++)
29           cnt+=upper_bound(sum+1,sum+1+m,-c[i]-d[j])-lower_bound(sum+1,sum+1+m,-c[i]-d[j]);
30       printf("%lld\n",cnt);
31       if (T) printf("\n");
32     }
33     return 0;
34 }
STL库实现

实现2:当然检索的高效算法 Hash也是可以的。我这里用的是闭散列表储存,即%一个大整数(一般是质数)之后放在相应的位置。若该位置已存储了其他数,则将这个数一直往后推直到一个空的位置。由于和的个数最多为n^2,小于列表的长度足够存储,那么就算和的值很大也不影响了。
   关于具体代码我再解释一些:1.常量的0x7fffff表示16进制的7fffff,f是15,即2^23;2.结构体哈希表重载了运算符[],这样就可以直接得到每个数的个数了;3.用&运算而不是%,可能是因为运算速度或存储空间,改成%就RE,但其实它们的意思是一样的,目的都是使数较集中的放在一起。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 const int N=4010;
 8 int a[N],b[N],c[N],d[N];
 9 
10 struct ha_map
11 {
12     static const int mod=0x7fffff;
13     int s[mod+1],d[mod+1];
14     void clear() {memset(s,0,sizeof(s));}
15     int& operator [] (int x)
16     {
17       int i;
18       for (i=x&mod;s[i]&&d[i]!=x;i=(i+1)&mod);
19       d[i]=x;
20       return s[i];
21     }
22 }ha;
23 
24 int main()
25 {
26     int T;
27     scanf("%d",&T);
28     while (T--)
29     {
30       int n;
31       scanf("%d",&n);
32       for (int i=1;i<=n;i++)
33         scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
34       ha.clear();
35       for (int i=1;i<=n;i++)
36         for (int j=1;j<=n;j++)
37           ha[a[i]+b[j]]++;
38       long long cnt=0;//long long
39       for (int i=1;i<=n;i++)
40         for (int j=1;j<=n;j++)
41           cnt+=ha[-c[i]-d[j]];
42       printf("%lld\n",cnt);
43       if (T) printf("\n");
44     }
45     return 0;
46 }
Hash实现

Hash的速度是STL库的4倍多~~ (-ω-*)

posted @ 2016-11-08 16:47  konjac蒟蒻  阅读(297)  评论(0编辑  收藏  举报