POJ 2002 -- Squares
Time Limit: 3500MS | Memory Limit: 65536K | |
Total Submissions: 20896 | Accepted: 8040 |
Description
So we all know what a square looks like, but can we find all possible squares that can be formed from a set of stars in a night sky? To make the problem easier, we will assume that the night sky is a 2-dimensional plane, and each star is specified by its x and y coordinates.
Input
Output
Sample Input
4
1 0
0 1
1 1
0 0
9
0 0
1 0
2 0
0 2
1 2
2 2
0 1
1 1
2 1
4
-2 5
3 7
0 0
5 2
0
Sample Output
1
6
1
Source
题意:
有一堆平面散点集,任取四个点,求能组成正方形的不同组合方式有多少。
相同的四个点,不同顺序构成的正方形视为同一正方形。
解题思路:
首先,不可以四个点四个点地枚举,看他们会不会组成正方形,肯定超时
我们枚举两个点,然后通过计算,得到能与他们组成正方形的剩下两个点的坐标
假设,我们知道了A(x1,y1)和B(x2,y2),那么通过全等三角形(两个红色三角形)的关系(如下图,请忽视我的渣字和渣图(..•˘_˘•..))
我们可以得到一种情况,
已知:(x1,y1) (x2,y2)
则:
x3=x1-(y1-y2) y3= y1+(x1-x2)
x4=x2-(y1-y2) y4= y2+(x1-x2)
另一种情况就是:
x3=x1+(y1-y2) y3= y1-(x1-x2)
x4=x2+(y1-y2) y4= y2-(x1-x2)
但是注意这种情况,会有重复计算的边,根据详细算法内容应作出最后处理
the magnitudes of the coordinates are less than 20000.
坐标的大小小于20000.
1)方法1(超时)
我们先枚举两个点,计算两个点之间的距离,距离%mod作为key值
处理冲突使用链地址法
如果遇到key值相同的两条边,则有可能会组成正方形,利用公式进行计算,如果组成了正方形,将计数++,将新加入的边插入Hash
好吧,上述算法很不争气的超时了
1 /*超时,待优化*/ 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 int ans; 6 int nodes[10001][2]; 7 const int mod = 20000; 8 class HashTable{ 9 public: 10 int node1,node2;//记录组成一条边的两个点的下标值 11 HashTable *next; 12 HashTable() 13 { 14 next = 0; 15 } 16 }; 17 18 HashTable *Hash[mod]; 19 20 bool isSquare(int x1,int x2,int x3,int x4) 21 { 22 if(nodes[x3][0] == nodes[x1][0]-(nodes[x1][1]-nodes[x2][1])//x3=x1-(y1-y2) 23 && nodes[x3][1] == nodes[x1][1]+(nodes[x1][0]-nodes[x2][0])// y3= y1+(x1-x2) 24 && nodes[x4][0] == nodes[x2][0]-(nodes[x1][1]-nodes[x2][1]) //x4=x2-(y1-y2) 25 && nodes[x4][1] == nodes[x2][1]+(nodes[x1][0]-nodes[x2][0]))//y4= y2+(x1-x2) 26 return true; 27 if(nodes[x3][0] == nodes[x1][0]+(nodes[x1][1]-nodes[x2][1])//x3=x1-(y1-y2) 28 && nodes[x3][1] == nodes[x1][1]-(nodes[x1][0]-nodes[x2][0])// y3= y1+(x1-x2) 29 && nodes[x4][0] == nodes[x2][0]+(nodes[x1][1]-nodes[x2][1]) //x4=x2-(y1-y2) 30 && nodes[x4][1] == nodes[x2][1]-(nodes[x1][0]-nodes[x2][0]))//y4= y2+(x1-x2) 31 return true; 32 if(nodes[x4][0] == nodes[x1][0]-(nodes[x1][1]-nodes[x2][1])//x3=x1-(y1-y2) 33 && nodes[x4][1] == nodes[x1][1]+(nodes[x1][0]-nodes[x2][0])// y3= y1+(x1-x2) 34 && nodes[x3][0] == nodes[x2][0]-(nodes[x1][1]-nodes[x2][1]) //x4=x2-(y1-y2) 35 && nodes[x3][1] == nodes[x2][1]+(nodes[x1][0]-nodes[x2][0]))//y4= y2+(x1-x2) 36 return true; 37 if(nodes[x4][0] == nodes[x1][0]+(nodes[x1][1]-nodes[x2][1])//x3=x1-(y1-y2) 38 && nodes[x4][1] == nodes[x1][1]-(nodes[x1][0]-nodes[x2][0])// y3= y1+(x1-x2) 39 && nodes[x3][0] == nodes[x2][0]+(nodes[x1][1]-nodes[x2][1]) //x4=x2-(y1-y2) 40 && nodes[x3][1] == nodes[x2][1]-(nodes[x1][0]-nodes[x2][0]))//y4= y2+(x1-x2) 41 return true; 42 return false; 43 44 } 45 46 void Insert(int x,int y) 47 { 48 int key = (nodes[x][0] - nodes[y][0])*(nodes[x][0] - nodes[y][0])%mod 49 + (nodes[x][1] - nodes[y][1])*(nodes[x][1] - nodes[y][1])%mod; 50 key = key%mod; 51 if(!Hash[key])//不发生冲突 52 {//直接将边插入 53 HashTable *temp = new HashTable; 54 temp->node1 = x;temp->node2 = y; 55 Hash[key] = temp; 56 }else{ 57 //发生冲突 58 HashTable *temp = Hash[key]; 59 if(temp->node1 != x && temp->node1 != y 60 && temp->node2 != x && temp->node2 != y 61 && isSquare(temp->node1,temp->node2,x,y))//如果存在相同的点,直接短路。不进入isSquare计算 62 {//构成正方形 63 ans++; 64 } 65 while(temp->next) 66 { 67 temp = temp->next; 68 if(temp->node1 != x && temp->node1 != y 69 && temp->node2 != x && temp->node2 != y 70 && isSquare(temp->node1,temp->node2,x,y)) 71 {//构成正方形 72 ans++; 73 } 74 } 75 temp->next = new HashTable; 76 temp->next->node1 = x; 77 temp->next->node2 = y; 78 } 79 } 80 int main() 81 { 82 int n; 83 while(cin>>n && n != 0) 84 { 85 memset(Hash,0,sizeof(Hash)); 86 ans = 0; 87 for(int i=1;i<=n;i++) 88 { 89 cin>>nodes[i][0]>>nodes[i][1]; 90 } 91 for(int i=1;i<=n-1;i++) 92 for(int j=i+1;j<=n;j++) 93 { 94 if(i == j) continue;//同一个点不能构成边 95 Insert(i,j);//将第i和j点组成的边插入Hash 96 } 97 cout<<ans/2<<endl; 98 } 99 return 0; 100 }
看样这种,先寻找相同长度的边,再检查点,查看其是否能组成正方形的方法,会产生太多多余计算。
2)方法二
看到了一篇文章POJ2002-Squares
她的方法是用点(x,y),x*x + y*y来标记点,然后枚举两个点,直接从hash表中,查找与这两个点组成正方形的其余两个点在hash中是否存在。
那位博主的代码如下(偷懒ing
1 //Memory Time 2 //652K 1438MS 3 4 #include<iostream> 5 using namespace std; 6 7 const int prime=1999; //长度为2n区间的最大素数 (本题n=1000) 8 9 //其他prime可取值: 10 // 1n 区间: 997 1704ms 11 // 2n 区间: 1999 1438ms 12 // 8n 区间: 7993 1110ms 13 // 10n 区间: 9973 1063ms 14 // 30n 区间: 29989 1000ms 15 // 50n 区间: 49999 1016ms 16 // 100n区间: 99991 1000ms 17 18 //为了尽量达到key与地址的一一映射,hash[]至少为1n, 19 //当为1n时,空间利用率最高,但地址冲突也相对较多,由于经常要为解决冲突开放寻址,使得寻找key值耗时O(1)的情况较少 20 //当n太大时,空间利用率很低,但由于key分布很离散,地址冲突也相对较少,使得寻找键值耗时基本为O(1)的情况 21 22 typedef class 23 { 24 public: 25 int x,y; 26 }Node; 27 28 typedef class HashTable 29 { 30 public: 31 int x,y; //标记key值对应的x,y 32 HashTable* next; //当出现地址冲突时,开放寻址 33 34 HashTable() //Initial 35 { 36 next=0; 37 } 38 }Hashtable; 39 40 Node pos[1001]; 41 Hashtable* hash[prime]; //hash[]是指针数组,存放地址 42 43 void insert_vist(int k) 44 { 45 int key=((pos[k].x * pos[k].x)+(pos[k].y * pos[k].y))%prime +1; //+1是避免==0 46 //使key从[0~1998]后移到[1~1999] 47 if(!hash[key]) 48 { 49 Hashtable* temp=new Hashtable; 50 temp->x=pos[k].x; 51 temp->y=pos[k].y; 52 hash[key]=temp; 53 } 54 else //hash[key]已存地址,地址冲突 55 { 56 Hashtable* temp=hash[key]; 57 58 while(temp->next) //开放寻址,直至next为空 59 temp=temp->next; 60 61 temp->next=new HashTable; //申请新结点,用next指向,记录x、y 62 temp->next->x=pos[k].x; 63 temp->next->y=pos[k].y; 64 } 65 return; 66 } 67 68 bool find(int x,int y) 69 { 70 int key=((x * x)+(y * y))%prime +1; 71 72 if(!hash[key]) //key对应的地址不存在 73 return false; 74 else 75 { 76 Hashtable* temp=hash[key]; 77 78 while(temp) 79 { 80 if(temp->x==x && temp->y==y) 81 return true; 82 83 temp=temp->next; 84 } 85 } 86 87 return false; 88 } 89 90 int main(void) 91 { 92 int n; 93 while(cin>>n) 94 { 95 if(!n) 96 break; 97 98 memset(hash,0,sizeof(hash)); //0 <-> NULL 99 100 for(int k=1;k<=n;k++) 101 { 102 cin>>pos[k].x>>pos[k].y; 103 insert_vist(k); //插入哈希表,标记散点 104 } 105 106 int num=0; //正方形的个数 107 for(int i=1;i<=n-1;i++) 108 for(int j=i+1;j<=n;j++) 109 { 110 int a=pos[j].x-pos[i].x; 111 int b=pos[j].y-pos[i].y; 112 113 int x3=pos[i].x+b; 114 int y3=pos[i].y-a; 115 int x4=pos[j].x+b; 116 int y4=pos[j].y-a; 117 118 if(find(x3,y3) && find(x4,y4)) 119 num++; 120 121 x3=pos[i].x-b; 122 y3=pos[i].y+a; 123 x4=pos[j].x-b; 124 y4=pos[j].y+a; 125 126 if(find(x3,y3) && find(x4,y4)) 127 num++; 128 } 129 130 cout<<num/4<<endl; //同一个正方形枚举了4次 131 } 132 return 0; 133 }