【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 }
实现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的速度是STL库的4倍多~~ (-ω-*)