PKU 2002 Squares(二维点哈希+平方求余法+链地址法)
题目大意:原题链接
给定平面上的N个点,求出这些点一共可以构成多少个正方形。
解题思路:
若正方形为ABCD,A坐标为(x1, y1),B坐标为(x2, y2),则很容易可以推出C和D的坐标。对于特定的A和B坐标,C和D可以在线段AB的上面或者下面,即有两种情况。
根据构造三角形全等可以得知(很简单,注意下细节,不要把坐标弄混就行)
CD在AB上面x3=x2+(y1-y2),y3=y2+(x2-x1); x4=x1+(y1-y2),y4=y1+(x2-x1);
CD在AB下面x3=x2-(y1-y2),y3=y2-(x2-x1); x4=x1-(y1-y2),y4=y1-(x2-x1);
因此只需要枚举点A和点B,然后计算出两种对应的C和D的坐标,判断是否存在即可。这样计算完之后得到的答案是正确答案的4倍,因为正方形的4条边都枚举了,所以答案要右移两位
Insert(int x,int y)采用平方求余法构造哈希函数,链地址法(用类邻接表法实现)处理冲突进行哈希
Judge(int x,int y)通过查找判断CD两点是否同时存在,即能判断是否可以构成正方形
#include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxn=1010; const int N=10007; int px[maxn],py[maxn]; struct Node { int x,y; int next; }node[N]; long long ans; int n,cur,Hash[N]; void Insert(int x,int y) { int h=(x*x+y*y)%N; node[cur].x=x; node[cur].y=y; node[cur].next=Hash[h]; Hash[h]=cur++; } bool Judge(int x,int y) { int h=(x*x+y*y)%N; int next=Hash[h]; while(next!=-1){ if(x==node[next].x&&y==node[next].y) return true; next=node[next].next; } return false; } int main() { while(cin>>n,n){ memset(Hash,-1,sizeof(Hash)); cur=0,ans=0; for(int i=0;i<n;i++){ cin>>px[i]>>py[i]; Insert(px[i],py[i]); } for(int i=0;i<n;i++){//先枚举考虑CD在AB上面 for(int j=i+1;j<n;j++){ int x3=px[j]+(py[i]-py[j]); int y3=py[j]+(px[j]-px[i]); int x4=px[i]+(py[i]-py[j]); int y4=py[i]+(px[j]-px[i]); if(Judge(x3,y3)&&Judge(x4,y4)) ans++; } } for(int i=0;i<n;i++){//再枚举考虑CD在AB下面 for(int j=i+1;j<n;j++){ int x3=px[j]-(py[i]-py[j]); int y3=py[j]-(px[j]-px[i]); int x4=px[i]-(py[i]-py[j]); int y4=py[i]-(px[j]-px[i]); if(Judge(x3,y3)&&Judge(x4,y4)) ans++; }//要判断C点和D点同时存在才能构成正方形 } ans>>=2; printf("%lld\n",ans); } }