POJ 2002 -- Squares

Posted Amysear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 2002 -- Squares相关的知识,希望对你有一定的参考价值。

Squares
Time Limit: 3500MS   Memory Limit: 65536K
Total Submissions: 20896   Accepted: 8040

Description

A square is a 4-sided polygon whose sides have equal length and adjacent sides form 90-degree angles. It is also a polygon such that rotating about its centre by 90 degrees gives the same polygon. It is not the only polygon with the latter property, however, as a regular octagon also has this property. 

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

The input consists of a number of test cases. Each test case starts with the integer n (1 <= n <= 1000) indicating the number of points to follow. Each of the next n lines specify the x and y coordinates (two integers) of each point. You may assume that the points are distinct and the magnitudes of the coordinates are less than 20000. The input is terminated when n = 0.

Output

For each test case, print on a line the number of squares one can form from the given stars.

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 }

 

 

 

以上是关于POJ 2002 -- Squares的主要内容,如果未能解决你的问题,请参考以下文章

POJ 2002 Squares 数学 + 必须hash

[poj2002]Squares_hash

E - Squares (POJ - 2002)

POJ 3347 Kadj Squares

POJ 3347 Kadj Squares(计算几何)

POJ3347:Kadj Squares——题解