1. 程式人生 > >POJ 2002 -- Squares

POJ 2002 -- Squares

查看 class csdn 枚舉 tinc 距離 put href 散點

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

Rocky Mountain 2004

題意:

有一堆平面散點集,任取四個點,求能組成正方形的不同組合方式有多少。

相同的四個點,不同順序構成的正方形視為同一正方形。

解題思路:

首先,不可以四個點四個點地枚舉,看他們會不會組成正方形,肯定超時

我們枚舉兩個點,然後通過計算,得到能與他們組成正方形的剩下兩個點的坐標

假設,我們知道了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